题目:http://poj.org/problem?id=2001
转自:http://hi.baidu.com/gdut_sai/item/3e953a13b0d381573b176ea3
描述:给你那么多字符串,然后让你找出最短的前缀字符串。但不能跟下面的单词的前缀相同。
样例:
输入:
carbohydrate
cart
carburetor
caramel
caribou
carbonic
cartilage
carbon
carriage
carton
car
carbonate
输出:
carbohydrate carboh
cart cart
carburetor carbu
caramel cara
caribou cari
carbonic carboni
cartilage carti
carbon carbon
carriage carr
carton carto
car car
carbonate carbona
思路:trie建树,设置一个变量num,计算每个单词出现的次数。查找的时候如果num = 1则说明这是第一次出现过的单词,直接返回。
在建tire的时候,设置一个num变量记录孩子节点的个数,如果孩子节点为1表示是一个单词节点,如果num>1表示是一个中间节点,继续搜索。
代码:
(1)采用采用链表的方式建Trie树
#include<iostream>
#include<cstdlib>
#include<cstdio>
using namespace std;
char str[1005][25];
struct Trinode{
int num;//记录孩子节点的个数
Trinode *link[26];
Trinode(int _num=0):num(_num)
{
for(int i=0;i<26;i++){
link[i] = NULL;
}
}
};
class Trie{
public:
Trie()
{
root = new Trinode(0);
}
void insert(char *tar)
{
char ch;
Trinode *ptr = root;
while(*tar){
ch = *tar-'a';
if(ptr->link[ch]==NULL){
ptr->link[ch] = new Trinode(1);//单词节点的num为1
}
else
ptr->link[ch]->num++;
ptr = ptr->link[ch];
tar++;
}
}
void seach(char *tar)
{
int i = 0;
char ch;
Trinode* ptr = root;
while(ch = *tar){
cout<<ch;
if(ptr->link[ch-'a']->num == 1)return;
ptr = ptr->link[ch-'a'];
tar++;
}
}
private:
Trinode *root;
};
class ShortestPrefixes{
public:
void solution()
{
int n,i;
Trie tree;
while(scanf("%s",str[n])!=EOF)
//cin>>n;
//for(int i=0;i<n;i++)
{
//scanf("%s",str[i]);
tree.insert(str[n]);
n++;
}
for(i=0;i<n;++i)
{
printf("%s ",str[i]);
tree.seach(str[i]);
printf("\n");
}
}
};
int main()
{
ShortestPrefixes poj2001;
poj2001.solution();
system("pause");
return 0;
}
(2)采用一维数组的方式建立Trie树
我的字典树有点慢诶 , 47ms Posted by jufusong at 2013-03-20 15:28:41 on Problem 2001 -------------------------------------------------------------------------------- # include <cstdio> # include <iostream> using namespace std ; char s [ 1100 ] [ 21 ] ; struct Dic { int time ; int next [ 30 ] ; } dic [ 1000000 ] ; int cnt ;//记录了当前数组中插入了多节点 void addword ( char * s ) { int len = strlen ( s ) ; int num = 0 ;//num用来遍历trie树 for ( int i = 0 ; i < len ; i ++ ) { int c = s [ i ] - 'a' ; if ( dic [ num ] . next [ c ] ) { num = dic [ num ] . next [ c ] ; dic [ num ] . time ++ ; } else { dic [ num ] . next [ c ] = cnt ; num = cnt ++ ; dic [ num ] . time ++ ; } } } void findword ( char * s ) { int len = strlen ( s ) ; int num = 0 ; for ( int i = 0 ; i < len ; i ++ ) { int c = s [ i ] - 'a' ; if ( dic [ num ] . time == 1 ) break ; putchar ( s [ i ] ) ; num = dic [ num ] . next [ c ] ; } } int main ( ) { cnt = 1 ; int n = 0 ; while ( gets ( s [ n ] ) ) { addword ( s [ n ++ ] ) ; } dic [ 0 ] . time = n ; for ( int i = 0 ; i < n ; i ++ ) { printf ( "%s " , s [ i ] ) ; findword ( s [ i ] ) ; printf ( "\n" ) ; } }
讨论区的这个哥们也是用这么建的
#include <string> #include <cstring> #include <cstdlib> #include <iostream> using namespace std; struct node { int cnt; short al[26]; }; int ptr=0; char s[1001][21]; node word[20010]; void Insert(char*s) { int now=0; int len=strlen(s); for(int i=0;i<len;i++) { if(word[now].al[s[i]-'a']==0) { word[now].al[s[i]-'a']=++ptr; now=ptr; } else now=word[now].al[s[i]-'a']; word[now].cnt++; } } void print(char*s) { int now=0; int len=strlen(s); printf("%s ",s); for(int i=0;i<len;i++) { now=word[now].al[s[i]-'a']; printf("%c",s[i]); if(word[now].cnt==1) break; } printf("\n"); } int main() { int i=0; while(gets(s[i])&&s[i][0]!='\0') Insert(s[i++]); for(int ii=0;ii<i;ii++) print(s[ii]); return 0; }
(3)另解:其实这道题大可不必如此费事,简单的比较就能搞定。给初学C++的兄弟们提供个思路:基本思路:先给字符串排序,之后对于每个字符串,分别和上下两个字符串比较(如果有的话),找到第一个字符不同,或者字符串终止的位置记录下来,则两个位置中比较靠后的,就是最短前缀串的位置。这样比较只需对相邻两字符串比较一次,算法复杂度为O(n),已经非常好了输出的时候注意:不要把'\0'也输出去,会WA的!
/*Source: < C++ Memory: 240K Time: 0ms>*/ #include<iostream> #include<cstring> #include<algorithm> //排序提速用的,为了0ms不惜一切代价 using namespace std; const int MAX = 1001; //要稍微开大一点,防止输入数据中有空行 struct Prefix{ char ch[24]; //字串,24个长度,也是适当的冗余 int PrefixPosition; //最短前缀位置 }str[MAX]; Prefix* pointer[MAX] = {NULL}; //用指针数组操作,排序时只交换指针 //不影响原来的字符串先后顺序 bool cmp(Prefix*a,Prefix*b){ //要返回bool类型,int不行 return strcmp(a->ch,b->ch) < 0; } int main(){ int count = 0,pos1 = 0,pos2 = 0; while(cin>>str[count].ch){ pointer[count] = &str[count]; ++count; } //sort the strings pointer in alphabetical order sort(pointer,pointer+count,cmp); //Compare for(int i = 0;i < count;++i){ //和前后比较,a[i]与a[i+1]的比较在a[i]的时候能用到,同样循环到a[i+1]的时候也能用到 if(i > 0)pos1 = pos2; //这样可减少一半的重复 pos2 = 0; if(i < count - 1) while(pointer[i]->ch[pos2] && pointer[i]->ch[pos2] == pointer[i+1]->ch[pos2]) ++pos2; //用最大的 pointer[i]->PrefixPosition = max(pos1,pos2); } for(int i = 0;i < count;++i){ printf("%s ",str[i].ch); for(int j = 0;j <= str[i].PrefixPosition;++j) //不要把'\0'打进去 if(str[i].ch[j])putchar(str[i].ch[j]); printf("\n"); } return 0; }
后记:菜鸟刚学会了Trie树的构造方法,还有就是最后这个哥们的想法独具匠心啊,膜拜。