例题
给你若干个单词,再给出若干次询问,每次询问给出一个字符串,让你求出以这个字符串为前缀的单词总共有多少个
先把所有单词建树,建的过程中顺便把所有前缀数量(也就是结点使用次数)统计出来,查询复杂度为O(len)。
AC代码:
int trie[1000010][26];
int ct = 1; //总结点数
int ans[1000010]; //统计每个节点的前缀数量
void Insert(string a)
{
int p = 0;
for (int i = 0; a[i]; i++)
{
int temp = a[i] - 'a';
if (trie[p][temp]) //如果这个前缀之前已经存在,那么直接移动到下一节点
p = trie[p][temp];
else //否则 创建一个新的结点 并移动到这一节点
p = trie[p][temp] = ct++;
ans[p]++; //统计该位置的前缀数
}
}
void get(string a)
{
int p = 0;
for (int i = 0; a[i]; i++)
{
int temp = a[i] - 'a';
if (trie[p][temp]) //如果能找到则继续找
p = trie[p][temp];
else //如果某一个位置找不下去了,那么输出0
{
p = 0;
break;
}
}
cout << ans[p] << endl;
}
int main()
{
string temp;
while (getline(cin, temp) && temp[0])
Insert(temp);
while (getline(cin, temp))
get(temp);
}
最多不超过五万个单词,单词长度也不会很长,那么就建字典树暴力判断每个单词的两个子串,都存在就可以分成两个子串组成。最坏的时间复杂度是O(len* len*50000)
AC代码:
int trie[1000000][26]; //13MB
bool is[1000000];
string S[50005];
int sum = 0;
int cnt = 1;
void Insert(string a)
{
int pos = 0;
for (int i = 0; a[i]; i++)
{
int temp = a[i] - 'a';
if (trie[pos][temp])
pos = trie[pos][temp];
else
pos = trie[pos][temp] = cnt++;
}
is[pos] = 1; //构成单词
}
bool get(string a)
{
int pos = 0;
for (int i = 0; a[i]; i++)
{
int temp = a[i] - 'a';
if (trie[pos][temp])
pos = trie[pos][temp];
else
return 0;
}
if (is[pos])
return 1;
return 0;
}
int main()
{
while (cin >> S[sum++])
Insert(S[sum - 1]);
for (int i = 0; i < sum; i++)
for (int j = 1; j < S[i].size(); j++)
if (get(S[i].substr(0, j)) && get(S[i].substr(j, S[i].size() - j)))
{
cout << S[i] << endl;
break;
}
return 0;
}