cpp
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
using namespace std;
typedef long long ll;
const int N = 10010 * 50, M = 1000010;
int tr[N][26], cnt[N], idx;
char str[M];
int q[N], ne[N];
void insert()
{
int p = 0;
for(int i = 0; str[i]; i ++)
{
int t = str[i] - 'a';
if(!tr[p][t])tr[p][t] = ++ idx;
p = tr[p][t];
}
cnt[p] ++;
}
void build()
{
int hh = 0, tt = -1;
for(int i = 0; i < 26; i ++)
{
if(tr[0][i])q[++ tt] = tr[0][i];
}
while(hh <= tt)
{
int t = q[hh ++];//i-1
for(int i = 0; i < 26; i ++)//p[i]
{
int c = tr[t][i];//i
if(!c)continue;
int j = ne[t];
while(j && !tr[j][i])j = ne[j];
if(tr[j][i])j = tr[j][i];
ne[c] = j;
q[++ tt] = c;
}
}
}
void solve()
{
int n;
cin >> n;
for(int i = 0; i < n; i ++)
{
cin >> str;
insert();
}
build();
int ans = 0;
cin >> str;
for(int i = 0, j = 0; str[i]; i ++)
{
int t = str[i] - 'a';
while(j && !tr[j][t])j = ne[j];
if(tr[j][t])j = tr[j][t];
int p = j;
while(p && cnt[p] != -1)
{
ans += cnt[p];
cnt[p] = -1;
p = ne[p];
}
}
cout << ans << endl;
}
int main()
{
IOS
int _;
cin >> _;
while(_ --)
{
memset(tr, 0, sizeof tr);
memset(cnt, 0, sizeof cnt);
memset(ne, 0, sizeof ne);
solve();
}
return 0;
}
核心思路是kmp的拓展,只是i++、j++什么的转换成了树的形式,初始化用bfs,每一点的初始化都是借助于该层以前的层进行的。
trie图优化:
cpp
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
using namespace std;
typedef long long ll;
const int N = 10010 * 50, M = 1000010;
int tr[N][26], cnt[N], idx;
char str[M];
int q[N], ne[N];
void insert()
{
int p = 0;
for(int i = 0; str[i]; i ++)
{
int t = str[i] - 'a';
if(!tr[p][t])tr[p][t] = ++ idx;
p = tr[p][t];
}
cnt[p] ++;
}
void build()
{
int hh = 0, tt = -1;
for(int i = 0; i < 26; i ++)
{
if(tr[0][i])q[++ tt] = tr[0][i];
}
while(hh <= tt)
{
int t = q[hh ++];//i-1
for(int i = 0; i < 26; i ++)//p[i]
{
/*
int c = tr[t][i];//i
if(!c)continue;
int j = ne[t];
while(j && !tr[j][i])j = ne[j];
if(tr[j][i])j = tr[j][i];
ne[c] = j;
q[++ tt] = c;
*/
int p = tr[t][i];
if(!p)tr[t][i] = tr[ne[t]][i];
else
{
ne[p] = tr[ne[t]][i];
q[++ tt] = p;
}
}
}
}
void solve()
{
int n;
cin >> n;
for(int i = 0; i < n; i ++)
{
cin >> str;
insert();
}
build();
int ans = 0;
cin >> str;
for(int i = 0, j = 0; str[i]; i ++)
{
int t = str[i] - 'a';
/*
while(j && !tr[j][t])j = ne[j];
if(tr[j][t])j = tr[j][t];
*/
j = tr[j][t];
int p = j;
while(p && cnt[p] != -1)
{
ans += cnt[p];
cnt[p] = -1;
p = ne[p];
}
}
cout << ans << endl;
}
int main()
{
IOS
int _;
cin >> _;
while(_ --)
{
memset(tr, 0, sizeof tr);
memset(cnt, 0, sizeof cnt);
memset(ne, 0, sizeof ne);
solve();
}
return 0;
}
ne[t]是回溯一次,tr[ne[t]][i]直接记录好了它下一个点的位置,存在儿子就到儿子,没有儿子就是记录的回溯好的点。
每个点的ne都被计算了。
纯板子:
cpp
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
using namespace std;
typedef long long ll;
const int N = 10010 * 50, M = 1000010;
int tr[N][26], cnt[N], idx;
char str[M];
int q[N], ne[N];
void insert()
{
int p = 0;
for(int i = 0; str[i]; i ++)
{
int t = str[i] - 'a';
if(!tr[p][t])tr[p][t] = ++ idx;
p = tr[p][t];
}
cnt[p] ++;
}
void build()
{
int hh = 0, tt = -1;
for(int i = 0; i < 26; i ++)
{
if(tr[0][i])q[++ tt] = tr[0][i];
}
while(hh <= tt)
{
int t = q[hh ++];//i-1
for(int i = 0; i < 26; i ++)//p[i]
{
int c = tr[t][i];//i
if(!c)continue;
int j = ne[t];
while(j && !tr[j][i])j = ne[j];
if(tr[j][i])j = tr[j][i];
ne[c] = j;
q[++ tt] = c;
}
}
}
void solve()
{
int n;
cin >> n;
for(int i = 0; i < n; i ++)
{
cin >> str;
insert();
}
build();
int ans = 0;
cin >> str;
for(int i = 0, j = 0; str[i]; i ++)
{
int t = str[i] - 'a';
while(j && !tr[j][t])j = ne[j];
if(tr[j][t])j = tr[j][t];
int p = j;
while(p && cnt[p] != -1)
{
ans += cnt[p];
cnt[p] = -1;
p = ne[p];
}
}
cout << ans << endl;
}
int main()
{
IOS
int _ = 1;
//cin >> _;
while(_ --)
{
//memset(tr, 0, sizeof tr);
//memset(cnt, 0, sizeof cnt);
//memset(ne, 0, sizeof ne);
solve();
}
return 0;
}