您的位置:首页 > 其它

HDOJ 2846 Repository (字典树变形)

2016-01-30 12:40 351 查看
点击打开链接

这道题的题意就是计算输入的单词被包含在几个源字符串中。

因为字典树是与前缀有关的,所以将每个源字符串的后缀都插入到字典树中去。这样在查找字典树是相当于从源字符串的中间位置开始查找。

有一个关键的问题就是如何判断两个单词是不是来自与同一个源字符串呢?比如源字符串add d既包含于dd又包含于d。但只能算一次。

这就要求我们在插入后缀单词的时候,带上一个身份标记表明来自哪一个编号的源字符串,如果在插入过程中,经过节点的时候,和节点上的标记num不同,那么更新num为这个新插入的单词的身份标记,并且累加这个节点的val。

上面这种操作是为了不重复累加val而服务的。为什么这么做就可以呢?因为来自同一个源字符串的所有后缀单词一定是连续地插入的。自己再体会下。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<string>
#define cg(x) x - 'a';
using namespace std;
int sum = 0, ans = 0;
const int MAXN = 1e6;
struct trie{
int next[26];
int val;
int num;
}tree[MAXN];
void insert(string s, int t)
{
int root = 0;
int l = s.size();
for (int i = 0; i < l; i++)
{
int c = cg(s[i]);
if (tree[root].next[c]) root = tree[root].next[c];
else
{
sum++;
tree[root].next[c] = sum;
root = sum;
memset(&tree[root], 0, sizeof(trie));
}
if (tree[root].num != t)
{
tree[root].num = t;
tree[root].val++;
}
}
}
void find(string s)
{
int root = 0, flag = 0;
int l = s.size();
for (int i = 0; i < l; i++)
{
int c = cg(s[i]);
if (tree[root].next[c]) root = tree[root].next[c];
else {flag = 1; break;}
}
if (flag == 0) ans += tree[root].val;
}
int main()
{
int n, l;
string s, s2;
cin >> n;
memset(&tree[0], 0, sizeof(trie));
for (int i = 1; i <= n; i++)
{
cin >> s;
l = s.size();
for (int j = 0; j < l; j++)
{
s2 = s.substr(j, l - j);
insert(s2, i);
}
}
cin >> n;
for (int i = 1; i <= n; i++)
{
ans = 0;
cin >> s;
find(s);
cout << ans << endl;
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: