题目: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树的构造方法,还有就是最后这个哥们的想法独具匠心啊,膜拜。
本文介绍了解决POJ2001问题的三种方法:使用Trie树进行高效查找;通过字符串排序及相邻比较实现简单快速求解;并提供了完整的代码示例。
412

被折叠的 条评论
为什么被折叠?



