(题解csy说得很详细了,这里复述一下,并补充 si,j 和 sufi,j 的具体求法)
假定我们使用KMP算法,对每个询问暴力求解,设:
prefi
表示
S
的的前缀
那么前缀
i
和后缀
那么对于询问
L
,
ans=∑i=1L∑j=Rn(pregi+sufj,prefi) .
但是复杂度太高了,我们发现
pregi
与
j
并无关系,所以我们可以利用前缀和
同样地,我们思考能不能用后缀和维护 sufj,prefi ,思考一下可以发现它只与 prefi 的值有关,我们并不需要知道他的i到底是多少,而 prefi 的值域是 [0,m) ,所以我们可以算出每个前缀中,每个适配指针有多少个,我们就可以 O(m) 地求到 ∑i=1L∑j=Rn(sufj,prefi
用
preg
和
suf
表示
preg
的前缀和
suf
的后缀,
si,j
表示前
i
个中
ans=(n−R+1)pregL+∑i=0m−1(sL,i×sufR,i)
新的问题是怎么求的
s
和
s
在对
(kmp板子同样来自csy)
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 5e4 + 10;
const int maxm = 110;
int nxt[maxn];
char x[maxn],y[maxm];
int n,m,q;
int suf[maxn][maxm];
int s[maxn][maxm];
int pre[maxn];
int l,r;
int nxt2[maxm][200];
int ismatch[maxm][200];
int main()
{
// freopen("1.in","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&q);
scanf("%s%s",x+1,y+1);
long long ans = 0;
int i,j;
for(nxt[1] = j = 0, i = 2; i <= m; nxt[i++] = j)
{
while(j && y[j+1] != y[i])
j = nxt[j];
if(y[j+1] == y[i])
j++;
}
for(j = 0,i = 1; i <= n; i++)
{
while(j && y[j+1] != x[i])
j = nxt[j];
if(y[j+1] == x[i])
j++;
pre[i] = pre[i - 1];
if(j == m)
pre[i]++, j = nxt[j];
for(int k = 0; k < m; k++)
s[i][k] = s[i-1][k];
s[i][j]++;
}
for(int i = 1; i <= n; i++)
pre[i] += pre[i-1];
for(int i = 0; i < m; i++)
for(int j = 'a'; j <= 'z'; j++)
{
ismatch[i][j] = 0;
int k = i;
while(k && y[k+1] != j)
k = nxt[k];
if(y[k+1] == j)
k++;
if(k == m)
k = nxt[k],ismatch[i][j] = 1;
nxt2[i][j] = k;
}
for(int i = 0; i < m; i++)
suf[n+1][i] = 0;
for(int i = n; i >= 1; i--)
for(int j = 0; j < m; j++)
suf[i][j] = ismatch[j][x[i]] + suf[i+1][nxt2[j][x[i]]];
for(int i = n; i >= 1; i--)
for(int j = 0; j < m; j++)
suf[i][j] += suf[i+1][j];
while(q--)
{
scanf("%d%d",&l,&r);
ans = 1ll * (n - r + 1) * pre[l];
for(int i = 0; i < m; i++)
ans += 1ll * s[l][i] * suf[r][i];
printf("%lld\n",ans);
}
}
return 0;
}