题目链接:k-string
Given a string S. K-string is the sub-string of S and it appear in the S at least K times.It means there are at least K different pairs (i,j) so that S i,S i+1… S j equal to this K-string. Given m operator or query:1.add a letter to the end of S; 2.query how many different K-string currently.For each query ,count the number of different K-string currently.
Input
The input consists of multiple test cases.
Each test case begins with a line containing three integers n, m and K(1<=n,K<=50000,1<=m<=200000), denoting the length of string S, the number of operator or question and the least number of occurrences of K-string in the S.
The second line consists string S,which only contains lowercase letters.
The next m lines describe the operator or query.The description of the operator looks as two space-separated integers t c (t = 1; c is lowercase letter).The description of the query looks as one integer t (t = 2).
Output
For each query print an integer — the number of different K-string currently.
Sample Input
3 5 2
abc
2
1 a
2
1 a
2
Sample Output
0
1
1
sol:
找了下多校的题解,标程写的是后缀数组加划分树?菜鸡并不会
对于后缀自动机的搞法,一个比较显然的在线做法是沿
p
a
r
e
n
t
parent
parent树暴力更新,然而一个全为
a
a
a的字符串就可以卡掉 。数据太水了
考虑离线倒着做,从最后添加的结点开始向上更新,发现那些出现次数已经小于K的结点之后都不会产生贡献,可以通过路径压缩,降低下次查询时沿
p
a
r
e
n
t
parent
parent树向上跳的开销。并查集实现即可。
code:
// #include<bits/stdc++.h>
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
#include <assert.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 5;
const int s_sz = 26;
#define fi first
#define se second
#define MP make_pair
#define pii pair<int, int>
char str[maxn];
int sum[maxn], tmp[maxn], pos[maxn], fa[maxn], sub[maxn];
int op[maxn];
ll que[maxn];
char ms[20];
int Find(int x)
{
return fa[x] == x ? x : fa[x] = Find(fa[x]);
}
struct SAM
{
int ch[maxn][s_sz];
int rt, sz, last;
int len[maxn], suf[maxn], r[maxn];
void init()
{
memset(ch, 0, sizeof(ch[0]) * (sz + 1));
memset(suf, 0, sizeof(int) * (sz + 1));
memset(r, 0, sizeof(int) * (sz + 1));
rt = sz = last = 1;
}
inline void add(int x, int c)
{
int p = last, np = ++sz;
last = np;
len[np] = x;
pos[x] = np;
while (p && !ch[p][c])
{
ch[p][c] = np;
p = suf[p];
}
if (!p)
{
suf[np] = rt;
return;
}
int q = ch[p][c];
if (len[q] == len[p] + 1)
suf[np] = q;
else
{
int nq = ++sz;
len[nq] = len[p] + 1;
memcpy(ch[nq], ch[q], sizeof(ch[q]));
suf[nq] = suf[q];
suf[np] = suf[q] = nq;
while (ch[p][c] == q)
{
ch[p][c] = nq;
p = suf[p];
}
}
}
inline int idx(char c)
{
return c - 'a';
}
inline void build(char *s)
{
int n = strlen(s);
for (int i = 0; i < n; i++)
{
add(i + 1, idx(s[i]));
}
}
inline void Topsort(int n)
{
memset(sum, 0, sizeof(int) * (n + 1));
for (int i = 1; i <= sz; i++)
sum[len[i]]++;
for (int i = 1; i <= n; i++)
sum[i] += sum[i - 1];
for (int i = 1; i <= sz; i++)
tmp[sum[len[i]]--] = i;
}
void get_right(char *s)
{
int u = rt;
int n = strlen(s);
for (int i = 0; i < n; i++)
{
u = ch[u][idx(s[i])];
r[u] = 1;
}
for (int i = sz; i; i--)
{
int u = tmp[i];
r[suf[u]] += r[u];
}
}
void sol(int n, int Q, int K)
{
ll ret = 0;
int L = strlen(str);
for (int i = 1; i <= sz; i++)
{
if (r[i] >= K)
ret += len[i] - len[suf[i]];
}
for (int i = 1; i <= sz; i++)
fa[i] = i, sub[i] = 0;
for (int i = Q; i; i--)
{
if (op[i] == 2)
que[i] = ret;
else
{
int p = pos[L--];
int fp = Find(p);
while (fp && r[fp] < K)
{
p = fa[fp] = suf[fp];
fp = Find(p);
}
if (fp == 0)
continue;
sub[fp]++;
while (fp && r[fp] - sub[fp] < K)
{
ret -= len[fp] - len[suf[fp]];
p = fa[fp] = suf[fp];
sub[suf[fp]] += sub[fp];
fp = Find(p);
}
}
}
}
} sam;
int main()
{
int n, Q, K;
while (scanf("%d%d%d", &n, &Q, &K) == 3)
{
scanf("%s", str);
for (int i = 1; i <= Q; i++)
{
int p;
scanf("%d", &p);
if (p == 1)
{
scanf("%s", ms);
str[n++] = ms[0];
op[i] = 1;
}
else
op[i] = 2;
}
str[n] = '\0';
sam.init();
sam.build(str);
sam.Topsort(n);
sam.get_right(str);
sam.sol(n, Q, K);
for (int i = 1; i <= Q; i++)
{
if (op[i] == 2)
printf("%lld\n", que[i]);
}
}
return 0;
}