首先考虑完整两串的匹配(我知道可以kmp,但是模糊匹配的思路需要从这里引出
假设短串a长为lena长串b长为lenb
我们知道两串如果完整匹配,他们每一个位置都要相同,即 a [ i ] − b [ y + i ] = 0 a[i]-b[y+i]=0 a[i]−b[y+i]=0
所以说a要完整匹配要满足:
∑ i = 0 l e n a a [ i ] − b [ y + i ] = 0 \sum_{i=0}^{lena} a[i]-b[y+i]=0 i=0∑lenaa[i]−b[y+i]=0
这是必要条件,不是充分条件,因为我们发现bd和cc按这种规则也能匹配,为了使偏差的增量恒为正,我们给他平方一下
∑ i = 0 l e n a ( a [ i ] − b [ y + i ] ) 2 = 0 \sum_{i=0}^{lena} (a[i]-b[y+i])^2=0 i=0∑lena(a[i]−b[y+i])2=0
这个时候复杂度显然是n^2的,考虑优化,我们把短串反转记为c,则 a [ i ] = c [ l e n a − i ] a[i]=c[lena-i] a[i]=c[lena−i]
∑ i = 0 l e n a ( c [ l e n a − i ] − b [ y + i ] ) 2 = 0 \sum_{i=0}^{lena} (c[lena-i]-b[y+i])^2=0 i=0∑lena(c[lena−i]−b[y+i])2=0
∑ i = 0 l e n a ( c [ l e n a − i ] ) 2 + ∑ i = 0 l e n a ( b [ y + i ] ) 2 − 2 ∑ i = 0 l e n a ( c [ l e n a − i ] b [ y + i ] ) = 0 \sum_{i=0}^{lena} (c[lena-i])^2+\sum_{i=0}^{lena}(b[y+i])^2-2\sum_{i=0}^{lena}(c[lena-i]b[y+i])=0 i=0∑lena(c[lena−i])2+i=0∑lena(b[y+i])2−2i=0∑lena(c[lena−i]b[y+i])=0
可以发现前两项能线性处理,后一项是卷积,能FFT
那么就结束了完整串匹配
这里也放个没交过的代码,数据范围1e5(如有错误敬请指出
#include
#define mod 950009857
#define gg 7
using namespace std;
char s1[200020],s2[200020];
long long g1[200020],g2[200020];
long long sum2[200020];
long long sum1;
int r[200020];
int lim;
long long kasumi(long long a,long long b)
{
long long ans=1;
while(b)
{
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
void NTT(long long *a,int kd)
{
for(int i=0;i<lim;i++)
{
if(i<r[i]) swap(a[i],a[r[i]]);
}
for(int mid=1;mid<lim;mid<<=1)
{
long long wn=kasumi(gg,(mod-1)/(mid<<1));
if(kd) wn=kasumi(wn,mod-2);
for(int i=0;i<lim;i+=(mid<<1))
{
long long w=1;
for(int j=0;j<mid;j++,w=wn*w%mod)
{
long long x=a[i+j];
long long y=a[i+j+mid]*w%mod;
a[i+j]=(x+y)%mod;
a[i+j+mid]=(x-y+mod)%mod;
}
}
}
if(kd)
{
long long inv1=kasumi(lim,mod-2);
for(int i=0;i<lim;i++)
{
a[i]=a[i]*inv1%mod;
}
}
}
int main()
{
scanf("%s",s1);
scanf("%s",s2);
int lena=strlen(s1);
int lenb=strlen(s2);
reverse(s1,s1+lena);
for(int i=0;i<lena;i++) g1[i]=s1[i]-'a'+1,sum1+=g1[i]*g1[i];
for(int i=0;i<lenb;i++) g2[i]=s2[i]-'a'+1,sum2[i]=((i>=1)?sum2[i-1]:0)+g2[i]*g2[i];
int cnt=0;
for(lim=1;lim<lenb*2;lim<<=1,cnt++);
for(int i=0;i<lim;i++)
{
r[i]=(r[i>>1]>>1)|((i&1)<<(cnt-1));
}
NTT(g1,0);NTT(g2,0);
for(int i=0;i<lim;i++)
{
g1[i]=g1[i]*g2[i]%mod;
}
NTT(g1,1);
for(int i=lena-1;i<=lenb;i++)
{
if(sum1+sum2[i]-((i>=lena)?sum2[i-lena]:0)-2*g1[i]==0)
{
printf("%d\n",i-lena+1);
}
}
}
然后开始考虑模糊匹配
如果是*的话要让他能跟所有配上,咋办呢,改下参数,使他能够决定整个匹配函数,怎么改呢
∑ i = 0 l e n a ( a [ i ] − b [ y + i ] ) 2 a [ i ] b [ y + i ] = 0 \sum_{i=0}^{lena} (a[i]-b[y+i])^2a[i]b[y+i]=0 i=0∑lena(a[i]−b[y+i])2a[i]b[y+i]=0
好的,我们继续反转字符串
∑ i = 0 l e n a ( c [ l e n a − i ] − b [ y + i ] ) 2 c [ l e n a − i ] b [ y + i ] = 0 \sum_{i=0}^{lena} (c[lena-i]-b[y+i])^2c[lena-i]b[y+i]=0 i=0∑lena(c[lena−i]−b[y+i])2c[lena−i]b[y+i]=0
∑ i = 0 l e n a ( c [ l e n a − i ] ) 3 b [ y + i ] + ∑ i = 0 l e n a ( b [ y + i ] ) 3 c [ l e n a − i ] − 2 ∑ i = 0 l e n a ( c [ l e n a − i ] b [ y + i ] ) 2 = 0 \sum_{i=0}^{lena} (c[lena-i])^3b[y+i]+\sum_{i=0}^{lena}(b[y+i])^3c[lena-i]-2\sum_{i=0}^{lena}(c[lena-i]b[y+i])^2=0 i=0∑lena(c[lena−i])3b[y+i]+i=0∑lena(b[y+i])3c[lena−i]−2i=0∑lena(c[lena−i]b[y+i])2=0
emmm,这下似乎没法线性搞了,不过没关系也就是多算几个卷积,复杂度同样是nlogn的
代码如下:
#include
#define mod 950009857
#define gg 7
using namespace std;
char s1[300020],s2[300020];
long long g1[1200020],g2[1200020],g3[1200020],g4[1200020],g5[1200020],g6[1200020];
int r[1200020];
int lim;
long long kasumi(long long a,long long b)
{
long long ans=1;
while(b)
{
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
void NTT(long long *a,int kd)
{
for(int i=0;i<lim;i++)
{
if(i<r[i]) swap(a[i],a[r[i]]);
}
for(int mid=1;mid<lim;mid<<=1)
{
long long wn=kasumi(gg,(mod-1)/(mid<<1));
if(kd) wn=kasumi(wn,mod-2);
for(int i=0;i<lim;i+=(mid<<1))
{
long long w=1;
for(int j=0;j<mid;j++,w=wn*w%mod)
{
long long x=a[i+j];
long long y=a[i+j+mid]*w%mod;
a[i+j]=(x+y)%mod;
a[i+j+mid]=(x-y+mod)%mod;
}
}
}
if(kd)
{
long long inv1=kasumi(lim,mod-2);
for(int i=0;i<lim;i++)
{
a[i]=a[i]*inv1%mod;
}
}
}
int tmp,lena,lenb,cnt=0;
int main()
{
scanf("%d%d",&lena,&lenb);
scanf("%s",s1);
scanf("%s",s2);
reverse(s1,s1+lena);
for(int i=0;i<lena;i++)
{
if(s1[i]=='*')
{
continue;
}
tmp=s1[i]-'a'+1;
g1[i]=tmp*tmp*tmp;
g3[i]=tmp;
g5[i]=tmp*tmp;
}
for(int i=0;i<lenb;i++)
{
if(s2[i]=='*')
{
continue;
}
tmp=s2[i]-'a'+1;
g2[i]=tmp;
g4[i]=tmp*tmp*tmp;
g6[i]=tmp*tmp;
}
for(lim=1;lim<lenb;lim<<=1,cnt++);
for(int i=0;i<lim;i++)
{
r[i]=(r[i>>1]>>1)|((i&1)<<(cnt-1));
}
NTT(g1,0);NTT(g2,0);NTT(g3,0);NTT(g4,0);NTT(g5,0);NTT(g6,0);
for(int i=0;i<lim;i++)
{
g1[i]=((g1[i]*g2[i]%mod+g3[i]*g4[i]%mod)%mod-2*g5[i]*g6[i]%mod+mod)%mod;
}
NTT(g1,1);
cnt=0;
for(int i=lena-1;i<lenb;i++)
{
if(g1[i]==0) cnt++;
}
printf("%d\n",cnt);
for(int i=lena-1;i<lenb;i++)
{
if(g1[i]==0) printf("%d ",i-lena+2);
}
}