CF528D Fuzzy Search
https://www.luogu.com.cn/problem/CF528D
字符串匹配\(/NTT\)
如果\(k=0\),那么\(KMP\)轻松解决
当\(k>0\)时,我们可以对每个字符分别考虑
例如样例,用\('A'\)去匹配:
\[AGCAATTCAT\\
ACAT
\]
将模式串和文本串中非\('A'\)字符当成\('*'\)
我们模拟样例进行操作:
\[A**AA***A*\\
A*A*\\
\because k=1\\
我们可以让文本串中的A拓展(将周围k范围内的字符全部变成A)\\
这可以用差分O(n)解决\\
那么文本串变为:AAAAAA*AAA\\
令A的值为1,非A字符值为0\\
如果模式串中含有q个A,要在i位置匹配,必然满足:\\
\sum_{j=1}^m S_{i+j-1}\times T_{j} = q\\
S与T下标差一致,与多项式类似,但是不能直接NTT,\\
我们把T倒过来就好了!\\
\sum_{j=1}^m S_{i+j-1}\times T_{m-j+1}^{reverse} = q\\
(i+j-1)+(m-j+1)=i+m\\
NTT解决!
\]
那么\(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;
}