最短前缀
题目:
总时间限制: 1000ms 内存限制: 65536kB 描述 一个字符串的前缀是从该字符串的第一个字符起始的一个子串。例如 "carbon"的字串是: "c", "ca", "car", "carb", "carbo", 和 "carbon"。注意到这里我们不认为空串是字串, 但是每个非空串是它自身的字串. 我们现在希望能用前缀来缩略的表示单词。例如, "carbohydrate" 通常用"carb"来缩略表示. 现在给你一组单词, 要求你找到唯一标识每个单词的最短前缀 在下面的例子中,"carbohydrate" 能被缩略成"carboh", 但是不能被缩略成"carbo" (或其余更短的前缀) 因为已经有一个单词用"carbo"开始 一个精确匹配会覆盖一个前缀匹配,例如,前缀"car"精确匹配单词"car". 因此 "car" 是 "car"的缩略语是没有二义性的 , “car”不会被当成"carriage"或者任何在列表中以"car"开始的单词. 输入 输入包括至少2行,至多1000行. 每行包括一个以小写字母组成的单词,单词长度至少是1,至多是20. 输出 输出的行数与输入的行数相同。每行输出由相应行输入的单词开始,后面跟着一个空格接下来是相应单词的没有二义性的最短前缀标识符。 样例输入 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
一开始想的是用结构体把每一个字符串按照长度升序排序,再遍历输出缩写,输出的缩写用book记录,但是没有考虑到 abcd abce abcde abced这种多分枝,不好记录。这里用的是字典树写的,之前没写过= =。以后再精简熟练一下。
#include<stdio.h> #include<string.h> int trie_book[2500],trie[2500][30],top=0,cur=0;//top是树的节点数(类似模拟链表的top),cur是输入的字符串个数,book是节点的出现次数 char s[1005][30]; void insert(char *s); void find(char *s); int main(){ while((scanf("%s",s[cur]))!=EOF){ insert(s[cur]); cur++; } for(int i=0;i<cur;i++){ find(s[i]); if(i!=cur-1) printf("\n"); } return 0; } void insert(char *s){ int root=0,ch,len=strlen(s);//ch表示某节点中代表该字母的子树入口,root即为当前节点,i为子节点 for(int i=0;i<len;i++){//遍历字符串 ch=s[i]-'a'; //将字符转为数字(用于节点储存) if(trie[root][ch]==0) trie[root][ch]=++top; //如果这个字母在当前节点的子节点没有出现过则创建一个节点 trie_book[trie[root][ch]]++;//该节点出现的次数+1 root=trie[root][ch];//进行字符串下一个字符的判断 即进入子节点 } } void find(char *s){ int ch,len=strlen(s),root=0; printf("%s ",s);//输出原字符串 for(int i=0;i<len;i++){ printf("%c",s[i]); ch=s[i]-'a'; if(trie_book[trie[root][ch]]==1) break;//当输出到的节点只出现过一次(也就是从这个字符开始 之前跟这个字符重复的字符串从这个字符的位置开始就不一样了,所以可以直接输出到这里就break;) root=trie[root][ch];//进入下一字节点 } }
感觉字典树的新节点构建和关联跟模拟链表是一样的?只不过字典树要先把字符转为数字= =
还有一个小细节,输入样例里面没有说怎么结束(我还看了半天题目?)。默认用的是EOF,那以后刷题没有表明结束方式的都用EOF结束了。