不管你看不看,我要坚持写。言而无信可耻,你随便。。。
题目链接:http://poj.org/problem?id=2001
题目大意:给你很多个字符串,让你找每个字符串的最短前缀,但这么多字符串前缀不能有相同的,要能唯一标识一个字符串,而且前缀长度尽量小。
思路: 一开始我们很容易想到用一大堆string.h里面的各种字符串处理的函数。但这类问题数据量一般很大,一般会超时,而且不好写(我是不会写也不敢那样写)。如果以前知道字典树是干嘛的话,很自然就想到字典树问题;
所谓字典树。。。
参考:http://baike.baidu.com/view/2759664.htm http://www.cnblogs.com/DiaoCow/archive/2010/04/19/1715337.html
没观众,没动力,没心情像前两篇讲dijkstra 和prim算法时那么详细了。。。
看完,理解了字典树了吧?那么,继续。我们可以记录一下每个节点被经过了几次,若nCount = 1,则说明到它当前缀的结尾肯定是唯一,否则不唯一;我猜猜猜:你如果是笨蛋的话,会问:
字符串abcde abcdefg 我让第一个的是a,第二个的是abcdef,这样a的 nCount>1,不行吗?不行,a不能唯一表示。前缀,前缀。。。
学长大神洪帮主说:字典树相当有用,今天才选这个主题。。。。
看代码:有详细注释:
//Accepted 564K 32MS C++ 1187B
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
struct TrieNode //c++里的struct 可以有函数的
{
int nCount; //记录有多少个分支经过该节点
struct TrieNode *next[26]; //题目中只有小写字母,所以是26,若有大小写则为52。若还有数字,自己用脚趾头想吧
TrieNode() //构造函数,就是在你申请一个节点空间时,把节点初始化
{
nCount = 0;
memset(next,0,sizeof(next));
}
};
char str[1010][30]; //看题目数据
TrieNode *root = NULL; //全局变量 ,尽管祝建华不建议多用,但在acm中,能方便就方便
void MakeTrie(char *s) //构造字典树
{
TrieNode * p = root;
TrieNode * tmp = NULL ;
for(size_t i = 0; i < strlen(s); i++)
{
if(p->next[ s[i] - 'a'] == NULL) //相当于一棵有26个儿子的树,当然你用到那个分支,就选那个s[i]-'a'就是这么来的
{
tmp = new TrieNode; //申请空间,初始化26个分支(memset啊啊啊),nCount置为0
p->next[ s[i] - 'a'] = tmp; //加到树上 看不懂,就去死吧
}
p = p->next[ s[i] - 'a']; //就像你写p = p->next一样
p->nCount ++; //记录有多少分支经过该节点
}
}
void SearchTrie(char *s)
{
TrieNode * p = root;
for(size_t i = 0; i < strlen(s); i++)
{
p = p->next[s[i] - 'a'];
printf("%c",s[i]);
if(p->nCount == 1) //==1就说明该节点只有1个分支经过,就说明以它为结尾的前缀是惟一的,果断跳出
break;
}
}
int main()
{
int num = 0;
root = new TrieNode;//自动调用构造函数初始化next[i]为NULL
while(cin >> str[num])
{
MakeTrie(str[num]);
num ++;
}
for(int i = 0; i < num; i++)
{
printf("%s ",str[i]);
SearchTrie(str[i]);
printf("\n");
}
return 0;
}