HDOJ 2072的五种做法 C strtok 字典树 map容器 set容器

HDU 2072

  本人做这个题目时花了太多时间,就整理汇集了见到的这几种方法。

第一种是用纯C做的:

#include<stdio.h>
#include<string.h>
int First_Not_Zero(char a[]);

int main()
{
	char sort_letter[100][100];
	char a[1100];
	while( fgets(a,1004,stdin)!=0)
	{
		if(a[0]=='#')
			break;
		if(a[0]=='\n')
		{
			printf("0\n");
			continue;
		}
		int now_f=0;
		int now_num=0;
		int num=0;
		int k=strlen(a);
		a[k]='\n';
		a[k-1]=' ';
		now_f=First_Not_Zero(a);
		for(int i=now_f; a[i]!='\n'; )
		{
			if(a[i]==' ')
			{
				a[i]=NULL;
				int j=0;
				for(; j<now_num; j++)
				{
					if(strcmp(&a[now_f],sort_letter[j])==0)
						break;
				}
				if(j==now_num)
				{
					strcpy(sort_letter[now_num],&a[now_f]);
					now_num++;
				}
				i++;
				while(a[i]==' ')
					i++;
				now_f=i;
			}
			else
				i++;
		}
		printf("%d\n",now_num);
	}
}

int First_Not_Zero(char a[])
{
	int i=0;
	for(; a[i]!='\n'; i++)
	{
		if(a[i]!=' ')
			break;
	}
	return i;//from HDUoj
}
第二种(strtok):

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int main()
{
    char s[1005],a[1005][1005];
    while(gets(s))
    {
        if(strcmp(s,"#")==0)
            break;
        int num,i,j,k,same=0;
        char *p;
        p=strtok(s," ");//strtok的函数使用
        for(num=0;p!=NULL;num++)
        {
            strcpy(a[num],p);//将字符串里面的字符转移到二维数组中
            p=strtok(NULL," ");
        }
        for(i=0;i<num;i++)
            for(j=i+1;j<num;j++)
            if(strcmp(a[i],a[j])==0)
            {
                same++;
                break;
            }
        cout<<num-same<<endl;
    }
    return 0;
}

<pre name="code" class="plain"><span style="font-size:24px;">strtok</span>
(百科)分解字符串为一组字符串。s为要分解的字符,delim为分隔符字符(如果传入字符串,则传入的字符串中每个字符均为分割符)。首次调用时,s指向要分解的字符串,之后再次调用要把s设成NULL。原型编辑char *strtok(char s[], const char *delim);功能编辑分解字符串为一组字符串。s为要分解的字符串,delim为分隔符字符串。例如:strtok("abc,def,ghi",","),最后可以分割成为abc def ghi.尤其在点分十进制的IP中提取应用较多。说明编辑strtok()用来将字符串分割成一个个片段。参数s指向欲分割的字符串,参数delim则为分割字符串中包含的所有字符。当strtok()在参数s的字符串中发现参数delim中包含的分割字符时,则会将该字符改为\0 字符。在第一次调用时,strtok()必需给予参数s字符串,往后的调用则将参数s设置成NULL。每次调用成功则返回指向被分割出片段的指针。返回值编辑从s开头开始的一个个被分割的串。当s中的字符查找到末尾时,返回NULL。如果查找不到delim中的字符时,返回当前strtok的字符串的指针。所有delim中包含的字符都会被滤掉,并将被滤掉的地方设为一处分割的节点。
 
 

第三种(字典树):

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct node
{
    int cnt;
    struct node *next[26];
    node()
    {
        cnt=0;
        memset(next,0,sizeof(next));
    }
};
node *root = NULL;
int sum;
void Createtrie(char *s)
{
    node *p=root;//根节点
    node *tmp=NULL;//不要忘了初始化指针
    int len=strlen(s);
    for(int i=0;i<len;i++)
    {
        int t=s[i]-'a';
        if(p->next[t]==NULL)
        {
            tmp=new node;
            p->next[t]=tmp;
        }
        p=p->next[t];
    }
    if(!p->cnt)
        sum++;
    p->cnt++;
}
int main()
{
    int len;
    char str[1000005],s[1005];
    while(gets(str))
    {
        if(strcmp(str,"#")==0)
            break;
        root = new node;//创建新节点
        len=strlen(str);
        sum=0;//初始化
        for(int i=0;i<len;i++)
            {
                while(str[i++]==' ');
                i--;
                if(i==len)
                    break;
                int j=0;
                while(str[i]!=' '&&i<len)
                    s[j++]=str[i++];
                s[j]='\0';//加上结束标志
                Createtrie(s);
            }
            cout<<sum<<endl;
    }
}
字典树

又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。

性质

编辑
它有3个基本性质:
根节点不包含字符,除根节点外每一个节点都只包含一个字符; 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串; 每个节点的所有子节点包含的字符都不相同。

基本操作

编辑
其基本操作有: 查找、插入和删除,当然删除操作比较少见。

实现方法

编辑
搜索字典项目的方法为:
(1) 从根结点开始一次搜索;
(2) 取得要查找关键词的第一个字母,并根据该字母选择对应的子树并转到该子树继续进行检索;
(3) 在相应的子树上,取得要查找关键词的第二个字母,并进一步选择对应的子树进行检索。
(4) 迭代过程……
(5) 在某个结点处,关键词的所有字母已被取出,则读取附在该结点上的信息,即完成查找。
其他操作类似处理

应用

编辑

串的快速检索

给出N个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。
在这道题中,我们可以用 数组枚举,用哈希,用字典树,先把熟词建一棵树,然后读入文章进行比较,这种方法效率是比较高的。

“串”排序

给定N个互不相同的仅由一个单词构成的英文名,让你将他们按 字典序从小到大输出
用字典树进行排序,采用 数组的方式创建字典树,这棵树的每个结点的所有儿子很显然地按照其字母大小排序。对这棵树进行先序遍历即可。

最长公共前缀

对所有串建立字典树,对于两个串的最长公共前缀的长度即他们所在的结点的公共祖先个数,于是,问题就转化为当时公共祖先问题。


第四种(map容器):

#include<iostream>
#include<map>
using namespace std;
int main()
{
    string n;
    map<string,int> m;
    while(getline(cin,n)&&n!="#")
    {
        string str;
        m.clear();
        int len=n.length();
        for(int i=0;i<len;i++)
        {
            if(isalpha(n[i]))
            {
                str.clear();
                int j;
                for(j=i;isalpha(n[j])&&j<len;j++)
                    str+=n[j];
                i=j;
                m[str]++;
            }
        }
        cout<<m.size()<<endl;
    }
}

第五种(set容器):

#include<iostream>
#include<set>
#include<string>
using namespace std;
int main()
{
    set<string> st;
    string str;
    char c;
    str.clear();
    while((c=cin.get())!='#')
    {
        while(c!=' '&&c!='\n')
        {
            str+=c;
            c=cin.get();
        }
        if(str.length())
        {
            st.insert(str);
            str.clear();
        }
        if(c=='\n')
        {
            cout<<st.size()<<endl;
            st.clear();
            str.clear();
        }
    }
    return 0;
}


你可能感兴趣的:(学习,HDU)