题意:求给定的多个串中是否存在,某个串是另一个串的前缀。
解题思路:使用Trie树。在每次插入一个串的同时判断。对于该问题,只存在以下两种情况:
1.当前插入的串的前缀是前面的某个串。对于这种情况,把前面插入的串的最后一个节点记为危险节点。那么在后面插入串的时候,如果经过危险节点,说明这种情况成立。
2.当前插入的串是前面某个串的前缀。对于这种情况,把插入串的最后一个节点单独拿出来判断,如果该串的最后一个节点已经被分配过地址,则说明这种情况成立。
这两个条件只要满足一个,即回答NO。
代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstring>
using namespace std;
struct Node
{
Node * child[10];
Node *prev;
bool bad;
Node() //构造函数中初始化。在静态分配时就能初始化。
{
memset(child, 0, sizeof(child));
prev = NULL;
bad = false;
}
};
int nodeCount; //用来指向数组中未分配的单元。
bool insertNode(Node *tree, char *s)
{
int i;
Node * p = tree;
for(i = 0; s[i+1]; i++)
{
if(p->child[s[i]-'0'] == 0)
{
p->child[s[i]-'0'] = tree + nodeCount; //将各个节点按次序分配到数组的相应位置。
nodeCount++;
}
p = p->child[s[i]-'0'];
if(p->bad) //如果遇到危险节点(某个字符串的最后一个节点),则说明该串含有前面的串。
{
return false;
}
}
bool ok;
if(p->child[s[i]-'0'] == 0)
{
p->child[s[i]-'0'] = tree + nodeCount;
nodeCount++; //一开始忘了写这一句,多次WA!!!。
ok = true;
}
else //如果最后一个节点已经被分配过,说明该串是前面串的前缀。
ok = false;
p = p->child[s[i]-'0'];
p->bad = true; //字符串最后一个节点是危险节点。
return ok;
}
int main()
{
int n, i, t;
bool ok;
scanf("%d", &t);
while(t--)
{
scanf("%d", &n);
char s[12];
Node Tree[100000]; //为了避免初始化,直接重新开,分配大小要合适,否则RA。
nodeCount = 1;
ok = true;
for(i = 0; i < n; i++)
{
scanf(" %s", s);
if(!insertNode(Tree+1, s))
{
ok = false;
}
}
if(ok)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}