题意:给你两个字符串S和T,每次询问(L, R)求出所有满足x<=L, y>=R的(x, y),S串的前x个字符和后y个字符接在一起后包含字符串T的个数之和
说实话。。这题会做题解也还是看不懂
这题瞎搞,主要是难在求不对可能会把答案算重复
设|T| = m,|S| = n;
①对串T求next和kmp
net[q]:表示T串以第q个字符结尾的后缀能匹配的最长前缀
len[i][a]:表示如果T串的前i个字符后面接上字母a,能匹配T的最长前缀(注意不能是整个T串)
cnt[i][j]:等于1表示如果T串的前i个字符后面接上字母j,刚好后缀是T串,很显然除了cnt[m-1][T[m]]其他都为0
②搞定之后就可以对S串dp了
pri[x]:T串在S串的前i个字符中出现pri[i]次
在求前缀和之前:dp[i][j]等于1表示S串以第i个字符结尾的后缀能最长匹配T串的前j个字符(当然j<m,不能满匹配)
也就是说在i相同的时候,有且只有一个j满足dp[i][j]==1,其他dp[i][]都为0
然后dp[i][j] += dp[i-1][j]
③搞定了<=L的前半部分,后半部分其实好办
在求前缀和之前:last[i][j]表示T串的前j个字符接上S串的后i个字符后包含last[i][j]个T串
然后last[i][j] += last[i+1][j]
最后对于每个询问(L, R)答案就是
#include<stdio.h>
#include<string.h>
#define LL long long
int net[105], len[105][26], cnt[105][26], pre[50005], dp[50005][105], last[50005][105];
char str[50005], jud[105];
int main(void)
{
LL ans;
int T, n, m, p, q, Q, i, j, x, y;
scanf("%d", &T);
while(T--)
{
scanf("%d%d%d%s%s", &n, &m, &Q, str+1, jud+1);
for(i=1;i<=n;i++)
str[i] -= 'a';
for(i=1;i<=m;i++)
jud[i] -= 'a';
p = 0;
for(q=2;q<=m;q++) //net[q]表示以第q个字符结尾的后缀能匹配的最长前缀
{
while(p && jud[p+1]!=jud[q])
p = net[p];
if(jud[p+1]==jud[q])
p++;
net[q] = p;
}
memset(cnt, 0, sizeof(cnt));
for(i=0;i<=m-1;i++)
{
for(j=0;j<26;j++)
{
p = i;
while(p && jud[p+1]!=j)
p = net[p];
if(jud[p+1]==j)
p++;
len[i][j] = p; //len[i][j]表示如果T串的前i个字符后面接上字母j,能匹配T的最长前缀
}
}
cnt[m-1][jud[m]] = 1; //cnt[i][j]==1表示如果T串的前i个字符后面接上字母j,刚好后缀是T串,很显然只有1种情况
len[m-1][jud[m]] = net[m]; //注意特判,能匹配的最长前缀不能是整个T串,很显然就是那1种情况
p = q = 0;
memset(dp, 0, sizeof(dp));
for(i=1;i<=n;i++)
{
q += cnt[p][str[i]];
p = len[p][str[i]];
pre[i] = pre[i-1]+q; //T串在S串的前i个字符中出现pri[i]次
dp[i][p] = 1;
}
for(i=1;i<=n;i++)
{
for(j=0;j<=m-1;j++)
dp[i][j] += dp[i-1][j]; //dp[i][j]表示T串的前j个字符作为最长前缀在S串的前i个字符中出现dp[i][j]次(当然j<m,不能满匹配),很显然这是个前缀和
}
/*for(i=1;i<=n;i++)
{
for(j=0;j<=m-1;j++)
printf("%d ", dp[i][j]);
printf("\n");
}*/
memset(last, 0, sizeof(last));
for(i=n;i>=1;i--)
{
if(str[i]==jud[m])
last[i][m-1]++;
for(j=0;j<=m-1;j++)
last[i][j] += last[i+1][len[j][str[i]]]; //last[i][j]表示T串的前j个字符接上S串的后i个字符能形成last[i][j]个完整的T串
}
for(i=n;i>=1;i--)
{
for(j=0;j<=m-1;j++)
last[i][j] += last[i+1][j]; //再求后缀和
}
while(Q--)
{
scanf("%d%d", &x, &y);
ans = (LL)(n-y+1)*pre[x];
for(i=0;i<m;i++)
ans += (LL)dp[x][i]*last[y][i];
printf("%lld\n", ans);
}
}
return 0;
}
/*
1
8 2 1
pppnotsb
pp
3 8
*/
本文深入解析了一种复杂的字符串匹配问题,通过动态规划等方法解决特定的字符串S与T的匹配计数问题。介绍如何利用next数组、前缀和后缀匹配技巧来避免答案重复计算,并给出详细的代码实现。
396

被折叠的 条评论
为什么被折叠?



