https://www.luogu.com.cn/problem/CF528D
如果\(k=0\),那么\(KMP\)轻松解决
当\(k>0\)时,我们可以对每个字符分别考虑
例如样例,用\(‘A‘\)去匹配:
将模式串和文本串中非\(‘A‘\)字符当成\(‘*‘\)
我们模拟样例进行操作:
那么\(4\)个字符呢?
很简单,每一个字符都必须能够在同一位置匹配,才能得出该位置是合法的
代码很好写
\(C++ Code:\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 200005
#define p 998244353
#define ll long long
using namespace std;
char s1[N],s2[N];
int s,n,m,k,ans[N],rev[N << 2],Ans,w[N];
ll a[N << 2],b[N << 2],G[2][24];
ll ksm(ll x,ll y)
{
ll ans=1;
while (y)
{
if (y & 1)
ans=ans*x%p;
x=x*x%p;
y >>=1;
}
return ans;
}
#define inv(x) (ksm(x,p-2))
void NTT(ll *a,int t)
{
for (int i=0;i<s;i++)
if (i<rev[i])
swap(a[i],a[rev[i]]);
for (int mid=1,o=1;mid<s;mid <<=1,o++)
for (int j=0;j<s;j+=(mid << 1))
{
ll g=1;
for (int k=0;k<mid;k++,g=g*G[t][o]%p)
{
ll x=a[j+k],y=g*a[j+k+mid]%p;
a[j+k]=(x+y)%p;
a[j+k+mid]=(x-y)%p;
}
}
}
void test(char c)
{
int q=0;
for (int i=0;i<m;i++)
if (s2[i]==c)
b[i]=1,q++; else
b[i]=0;
for (int i=0;i<=n;i++)
w[i]=0;
for (int i=0;i<n;i++)
if (s1[i]==c)
w[max(i-k,0)]++,w[min(i+k,n-1)+1]--;
s=0;
for (int i=0;i<n;i++)
{
s+=w[i];
if (s>0)
a[i]=1; else
a[i]=0;
}
int l=0;
s=1;
while (s<n+m)
{
l++;
s <<=1;
}
for (int i=n;i<s;i++)
a[i]=0;
for (int i=m;i<s;i++)
b[i]=0;
for (int i=0;i<s;i++)
rev[i]=(rev[i >> 1] >> 1) | ((i & 1) << (l - 1));
NTT(a,0);
NTT(b,0);
for (int i=0;i<s;i++)
a[i]=a[i]*b[i]%p;
NTT(a,1);
ll t=inv(s);
for (int i=0;i<s;i++)
a[i]=a[i]*t%p,a[i]=(a[i]%p+p)%p;
for (int i=0;i<s;i++)
if (a[i]==q&&i>=m-1&&i<n)
ans[i]++;
}
void Pre()
{
G[0][23]=ksm(3,(p-1)/(1 << 23));
G[1][23]=inv(G[0][23]);
for (int i=22;i>=1;i--)
{
G[0][i]=G[0][i+1]*G[0][i+1]%p;
G[1][i]=G[1][i+1]*G[1][i+1]%p;
}
}
int main()
{
Pre();
scanf("%d%d%d",&n,&m,&k);
scanf("%s%s",s1,s2);
for (int i=0;i<=m/2-1;i++)
swap(s2[i],s2[m-i-1]);
test(‘A‘);
test(‘G‘);
test(‘C‘);
test(‘T‘);
for (int i=0;i<n;i++)
if (ans[i]==4)
Ans++;
printf("%d\n",Ans);
return 0;
}
原文:https://www.cnblogs.com/GK0328/p/13414173.html