题意:给你一个字符串,和一个k,问你字符串中恰好出现k次的子串有多少种。
思路:HDU4641的简化版,HDU4641求的是>=k次的子串个数,所以现在我们只需跑两次计算出>=k次的和>=k+1次的,两者相减即位恰好出现k次的子串个数。
具体思路参考HDU4641的就行:点击打开链接
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+5;
int root,last;
int tots;
int l;
int sv[maxn*2];
struct query
{
int a;
ll ans;
}qu[maxn];
struct sam_node
{
int fa,son[26];
int len;
void init(int _len)
{
len = _len;
fa = -1;
memset(son,-1,sizeof(son));
}
}t[maxn*2];
void sam_init()
{
tots = 0;
root = last = 0;
t[tots].init(0);
}
void extend(int w)
{
int p = last;
int np = ++tots;
t[tots].init(t[p].len+1);
sv[l] = np;
int q, nq;
while(p != -1 && t[p].son[w] == -1)
{
t[p].son[w] = np;
p = t[p].fa;
}
if(p == -1) t[np].fa = root;
else
{
q = t[p].son[w];
if (t[p].len+1 == t[q].len) t[np].fa=q;
else
{
nq = ++tots;
t[nq].init(0);
t[nq] = t[q];
t[nq].len = t[p].len+1;
t[q].fa = nq;
t[np].fa = nq;
while(p!=-1&&t[p].son[w]==q)
{
t[p].son[w] = nq;
p = t[p].fa;
}
}
}
last = np;
}
int w[maxn], r[maxn*2];
void topsort()
{
for(int i = 0; i <= l; ++i) w[i] = 0;
for(int i = 1; i <= tots; ++i) w[t[i].len]++;
for(int i = 1; i <= l; ++i) w[i] += w[i-1];
for(int i = tots; i >= 1; --i) r[w[t[i].len]--] = i;
r[0] = 0;
}
int dp[maxn*2];
char s[maxn];
int main()
{
int n, k, p;
int _;
cin >> _;
while(_--)
{
scanf("%d %s", &k, &s);
int tl = strlen(s);
l = 0;
sam_init();
for(int i = 0; i < tl; ++i)
{
++l;
extend(s[i]-'a');
}
for(int i = 0; i <= tots; ++i) dp[i] = 0;
topsort();
p = root;
for(int i = 0; i < l; ++i)
{
p = t[p].son[s[i]-'a'];
dp[p]++;
}
for(int i = tots; i >= 1; --i)
{
p = r[i];
if(t[p].fa != -1) dp[t[p].fa] += dp[p];
}
ll ans1 = 0, ans2 = 0;
for(int i = 1; i <= tots; ++i)
if(dp[i] >= k)
ans1 += t[i].len-t[t[i].fa].len;
for(int i = 1; i <= tots; ++i)
if(dp[i] >= k+1)
ans2 += t[i].len-t[t[i].fa].len;
printf("%lld\n", ans1-ans2);
}
return 0;
}