BZOJ#4503. 两个串


4503: 两个串

Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 1128  Solved: 457

Description

兔子们在玩两个串的游戏。给定两个字符串S和T,兔子们想知道T在S中出现了几次,
分别在哪些位置出现。注意T中可能有“?”字符,这个字符可以匹配任何字符。

Input

两行两个字符串,分别代表S和T

Output

第一行一个正整数k,表示T在S中出现了几次
接下来k行正整数,分别代表T每次在S中出现的开始位置。按照从小到大的顺序输出,S下标从0开始。

Sample Input

bbabaababaaaaabaaaaaaaabaaabbbabaaabbabaabbbbabbbbbbabbaabbbababababbbbbbaaabaaabbbbbaabbbaabbbbabab
a?aba?abba

Sample Output

0

HINT

S 长度不超过 10^5, T 长度不会超过 S。 S 中只包含小写字母, T中只包含小写字母和“?”
 

Problem:

给定一个文本串(只包含a,b)和一个匹配串,匹配串中会有通用符(与任意字符都匹配),问文本串中有多少和匹配串相同的子串

 



Solution:
先看没有通用符怎么判断匹配
怎么判断一个字符匹配?
它们相等就匹配
怎么判断一个字符串匹配?
它们中每个对应的字符匹配就匹配
那么在这里可得一个式子:
Σ(a[i]-b[i])=0
可是会出现前面与后面刚好相加为0的情况
那么再改进一下:
Σ[(a[i]-b[i])^2]=0

如果有通用符怎么办?
Σ[b[i]*(a[i]-b[i])^2]=0 (b为匹配串,赋通用符为0,那么如果它为通用符,或者和a相等就匹配)

再化解一下式子:
ans=Σb[i]*(a[i]*a[i]-2*a[i]*b[i]+b[i]*b[i]
=Σa[i]*a[i]*b[i]-Σ2*a[i]*b[i]*b[i]+Σb[i]*b[i]*b[i]

式子解决了,我们怎么求?
看这个形式,如果我们把b反一下
我们就可以用卷积求得

可能没想清楚的同学会问,怎么保证卷积算出来的刚好就是一段连续的len2长度的子串
那么可以想一下,举个例子:
a=0 1 2 3 4 5 /b=0 1 2
当正在找以5为结尾的子串时
我们的卷积算的是Σa[i]+b[j](i+j==5)
我们b的j只会有0,1,2
那么我们的a只会选到相加等于5的编号的值,也就是5,4,3
可以看到我们刚好就是选到这个子串

ok!

 


 

 附上代码:

#include<bits/stdc++.h>
using namespace std;
const double pi=acos(-1);
const int N=880010;
char s1[N],s2[N];
int n;

struct Complex
{
    double x,i;
    Complex(){}
    Complex(double a,double b) {x=a;i=b;}
}A[N],B[N],C[N];
Complex operator + (Complex a,Complex b) {return Complex(a.x+b.x,a.i+b.i);}
Complex operator - (Complex a,Complex b) {return Complex(a.x-b.x,a.i-b.i);}
Complex operator * (Complex a,Complex b) {return Complex(a.x*b.x-a.i*b.i,a.x*b.i+a.i*b.x);}


int rev[N];
void FFT(Complex *a,int t)
{
    for(int i=0;i<n;i++) if(rev[i]>i) swap(a[i],a[rev[i]]);
    for(int i=1;i<n;i<<=1)
    {
        Complex wn(cos(2*pi/(i<<1)),t*sin(2*pi/(i<<1)));
        for(int j=0;j<n;j+=(i<<1))
        {
            Complex w(1,0),t0,t1;
            for(int k=0;k<i;k++) 
            {
                t0=a[j+k];t1=w*a[i+j+k];
                a[j+k]=t0+t1;
                a[i+j+k]=t0-t1;
                w=w*wn;
            }
        }
    }
}

int ans[N],cnt;
int a[N],b[N];
int main()
{
    freopen("a.in","r",stdin);
    scanf("%s%s",s1,s2);
    int len1=strlen(s1),len2=strlen(s2);
    n=1;
    
    int len=0;
    while(n<len1*2) n<<=1,len++;
    rev[0]=0;for(int i=1;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(len-1));
    
    for(int i=0;i<len1;i++) a[i]=s1[i]-'a'+1;
    for(int i=0;i<len2;i++) b[len2-i-1]=s2[i]=='?'?0:s2[i]-'a'+1;
    for(int i=0;i<n;i++) A[i]=Complex(b[i]*b[i]*b[i],0),B[i]=Complex(1,0);
    FFT(A,1);FFT(B,1);
    
    for(int i=0;i<n;i++) C[i]=A[i]*B[i];
    for(int i=0;i<n;i++) A[i]=Complex(2*a[i],0),B[i]=Complex(b[i]*b[i],0);
    FFT(A,1);FFT(B,1);
    for(int i=0;i<n;i++) C[i]=C[i]-A[i]*B[i];
    for(int i=0;i<n;i++) A[i]=Complex(a[i]*a[i],0),B[i]=Complex(b[i],0);
    FFT(A,1);FFT(B,1);
    for(int i=0;i<n;i++) C[i]=C[i]+A[i]*B[i];
    FFT(C,-1);
    
    for(int i=0;i<n;i++) C[i].x=(int)(C[i].x/n+0.5);
    for(int i=len2-1;i<len1;i++) if(C[i].x==0) ans[++cnt]=i-len2+1;
    printf("%d\n",cnt);
    for(int i=1;i<=cnt;i++) printf("%d\n",ans[i]);
    return 0;
}
View Code

 


 

posted @ 2018-05-18 22:06  Heey  阅读(212)  评论(0编辑  收藏  举报