#include <stdio.h>
#include <string.h>
#include <vector>
#include <string>
#include <cstdlib>
#include <iostream>
using namespace std;
const int CHAR_NUM = 26;
struct tireNode{
char val[22];
struct tireNode * tireChildNode[CHAR_NUM];
int appearTime;
int byTime;
int childNum;
int index;
};
class stringInfo {
public:
string orginal;
string abbv;
stringInfo(const string & str): orginal(str), abbv("") {
}
};
typedef struct stringInfo stringInfo;
typedef struct tireNode tireNode;
vector<stringInfo> V1;
tireNode * tireTreeHead;
void buildTireTree(char * word, int index) {
if (!tireTreeHead) {
tireTreeHead = (tireNode *)malloc(sizeof(tireNode));
memset(tireTreeHead, 0, sizeof(tireNode));
}
int wordLength = strlen(word);
tireNode * currentNode = tireTreeHead;
for (int i = 0; i < wordLength; i++) {
if (!currentNode->tireChildNode[word[i]-'a']) {
currentNode->tireChildNode[word[i]-'a'] = (tireNode *)malloc(sizeof(tireNode));
memset(currentNode->tireChildNode[word[i]-'a'], 0, sizeof(tireNode));
strcpy(currentNode->tireChildNode[word[i]-'a']->val, currentNode->val);
currentNode->childNum++;
currentNode->tireChildNode[word[i]-'a']->val[strlen(currentNode->val)] = word[i];
currentNode->tireChildNode[word[i]-'a']->index = index;
}
currentNode = currentNode->tireChildNode[word[i]-'a'];
(currentNode->byTime)++;
}
(currentNode->appearTime)++;
// printf("build %s %d\n", currentNode->val, currentNode->appearTime);
}
void getShortsetPrefix(tireNode * currentNode) {
if (!currentNode) {
return;
}
if (currentNode->byTime == 1) {
// printf("1 %d %s %s\n", currentNode->index, V1.at(currentNode->index).orginal.c_str(), currentNode->val);
V1.at(currentNode->index).abbv = currentNode->val;
return;
}
// if (currentNode->appearTime) {
// // printf("%s %s\n", currentNode->val, possiblePrefix.c_str());
// // printf("2 %s %s\n", V1.at(currentNode->index).c_str(), currentNode->val);
// }
for (int i = 0; i < CHAR_NUM; i++) {
if (currentNode->tireChildNode[i]) {
getShortsetPrefix(currentNode->tireChildNode[i]);
}
}
}
void printRes(vector<stringInfo> V) {
if (!V.size()) {
cout<<endl;
// printf("\n");
}
vector<stringInfo>::iterator it = V.begin();
for (;it != V.end(); it++) {
if (it->abbv.empty()) {
cout<<it->orginal<<" "<<it->orginal<<endl;
} else {
cout<<it->orginal<<" "<<it->abbv<<endl;
// printf("%s %s\n", it->orginal.c_str(), it->abbv.c_str());
}
}
}
int main() {
char word[22] = {0};
tireTreeHead = NULL;
V1.clear();
while(scanf("%s", word) != EOF) {
// if (!strcmp(word, "end")) {
// break;
// }
V1.push_back(stringInfo(word));
buildTireTree(word, V1.size()-1);
}
getShortsetPrefix(tireTreeHead);
printRes(V1);
return 0;
}
G++ 1224K 47MS
tire树系列题, 这次用的是动态tire树,静态应该更快,这次也让我意识到,tire树建树本身不是题考察的重点,而是在建立好tire树以后,
如何用这棵树得到最后想要的结果。
题意是得到能无疑义表示该单词的缩写, 想了好久,不知道该如何表示这种abbv,
后来才想明白:
tire树其实是一条条字符串 顺序字符分解 所经过的路径的组合, 那么如何唯一的标示一个字符串? 2种case:
case1: 单词的整个路径都被其他的单词路过,及该单词必是其他单词的前缀,那么唯一表示办法abbv就是单词本身。
case2: 单词在某个字符以后所经过的路径没有被其他单词所经过过,那么唯一标示此单词的abbv就是前面的共享的前缀将上自己独有路径的第一个字符即可.
自己对tire树还是理解不够深入,从路径角度思考,就好理解很多。
那么在有了上面这个条件以后,就好办了, 只需为每个tire node加上一个byTime 来记录有多少个字符串经过此处,
然后在后面遍历此tire数时,察看当前node的byTime是否为1即可,如果是1,那么必定是某个字符串的独有路径,此node所记录的累加字符串即为该字符串的最短abbv,
并且直接return(这条路已经不需要再check了,可以理解为剪枝),遍历其他的tirenode。
本题比较烦人的是最后的输出,
还要按照原来的顺序输出原字符串与abbv, 及不但要得到abbv,还要能记录该abbv是对应于哪个原字符串。
我搞了一个很麻烦的法子,在原字符串输入时,就俺顺序为其分配相应的index,同时在建立tire树的时候,当有新路径被开辟时,tirenode中的index值会设置为此原字符串的index,这样就建立了起来映射关系,
而为了那些整个路径都被共享的原字符串(不能依靠byTime来区分),index在这种情况下也不能用,因为此公共路径的index在输入的时候会被改写多次。
因此就搞了一个复杂的class,存储orginal和addv, addv默认为空, 而original只有在上面遇到独有路径时才会根据node的index来设置vector中的相应的addv。
这样,最后遍历的时候,如果遇到abbv为空的情况,直接输出orginal orginal即可。
一开始WA了几次,原因是设置node的index的时候给了当前node,其实应该设给新建出来的node。
还要找找更好的办法,这个法子太ugly了.
#include <stdio.h>
#include <string.h>
#include <vector>
#include <string>
#include <cstdlib>
#include <iostream>
using namespace std;
const int CHAR_NUM = 26;
struct tireNode{
char val[22];
struct tireNode * tireChildNode[CHAR_NUM];
int appearTime;
int byTime;
int childNum;
};
typedef struct stringInfo stringInfo;
typedef struct tireNode tireNode;
vector<string> V1;
tireNode * tireTreeHead;
void buildTireTree(char * word) {
if (!tireTreeHead) {
tireTreeHead = (tireNode *)malloc(sizeof(tireNode));
memset(tireTreeHead, 0, sizeof(tireNode));
}
int wordLength = strlen(word);
tireNode * currentNode = tireTreeHead;
for (int i = 0; i < wordLength; i++) {
if (!currentNode->tireChildNode[word[i]-'a']) {
currentNode->tireChildNode[word[i]-'a'] = (tireNode *)malloc(sizeof(tireNode));
memset(currentNode->tireChildNode[word[i]-'a'], 0, sizeof(tireNode));
strcpy(currentNode->tireChildNode[word[i]-'a']->val, currentNode->val);
currentNode->childNum++;
currentNode->tireChildNode[word[i]-'a']->val[strlen(currentNode->val)] = word[i];
}
currentNode = currentNode->tireChildNode[word[i]-'a'];
(currentNode->byTime)++;
}
(currentNode->appearTime)++;
}
void getShortsetPrefix(string word) {
tireNode * currentNode = tireTreeHead;
int wordLength = word.size();
for (int i = 0; i < wordLength; i++) {
if (currentNode->byTime == 1) {
cout<<word<<" "<<currentNode->val<<endl;
return;
} else {
if (currentNode->tireChildNode[word[i]-'a']) {
currentNode = currentNode->tireChildNode[word[i]-'a'];
}
}
}
cout<<word<<" "<<word<<endl;
}
void printRes(vector<string> V) {
if (!V.size()) {
cout<<endl;
}
vector<string>::iterator it = V.begin();
for (;it != V.end(); it++) {
getShortsetPrefix(*it);
}
}
int main() {
char word[22] = {0};
tireTreeHead = NULL;
V1.clear();
while(scanf("%s", word) != EOF) {
// if (!strcmp(word, "end")) {
// break;
// }
V1.push_back(word);
buildTireTree(word);
}
printRes(V1);
return 0;
}
G++ 47ms
果然是我SB了,把问题复杂化了。。。
在用vector记录了输入的字符串以后,在输出的时候,只需遍历此vector,
用vector中保存的orginal 字符串对tire数组进行处理即可:
顺序遍历original的字符, 根据字符值沿着tire树中保存的路径一直向下走,沿路检查每个经过node的byTime,如果遇到=1的就输出此orginal abbv return。
否则,如果遍历完都找不到byTime == 1的node,那么可以就直接输出orginal original。