SGU 284 Grammar(KMP)

题目链接:http://acm.sgu.ru/problem.php?contest=0&problem=284

题意:给出n。其中数字1到n为非终结符,字母a和b为终结符。每个非终结符可以被该非终结符代表的字符串替换。对于数字i,i代表的字符串的若有非终结符j,则j<i。给出n代表的字符串p,再给出一个字符串s(只含有a和b)。将p用所有的非终结符用相应的串替换后得到最终的串q,问s在q中出现多少次?

思路:总的来说是一个不断的KMP匹配。从1到n,依次将每个数字代表的串替换为终结符并随时匹配,记录匹配个数。





const int MAX=505;

const int N=35;

BigNum ans[MAX];

char str[MAX],now[MAX*MAX*50],L[N][MAX],R[N][MAX];

i64 n,len,next[MAX],size[MAX],top,last;

int num[N][MAX];

bool can[MAX];



void calNext(char* str,i64* next,int len)

{

    int i,j;

    for(i=2,j=0,next[1]=0;i<=len;i++)

    {

        while(j&&str[j+1]!=str[i])j=next[j];

        if(str[j+1]==str[i]) j++;

        next[i]=j;

    }

}



int match(const char* a,int num)

{

    int tot=0,j=0,i;

    for(i=0;i<num;i++)

    {

        while(j&&str[j+1]!=a[i]) j=next[j];

        if(str[j+1]==a[i]) j++;

        if(j==len) tot++,j=next[j];

    }

    return tot;

}



int main()

{

    RD(n);RD(str+1);

    len=strlen(str+1);

    calNext(str,next,len);



    int i,j,k,t,Len;

    for(i=1;i<=n;i++)

    {

        RD(num[i][0]);

        gets(now);

        Len=strlen(now);

        k=0;

        for(j=1;j<=num[i][0];j++)

        {

            while(now[k]==' ') k++;

            if(now[k]=='a'||now[k]=='b') num[i][j]=now[k++];

            else

            {

                t=0;

                while(now[k]!=' '&&k<Len) t=t*10+now[k++]-'0';

                num[i][j]=t;

            }

        }

    }

    for(i=1;i<=n;i++)

    {

        top=0;

        last=0;

        for(j=1;j<=num[i][0];j++)

        {

            if(num[i][j]=='a'||num[i][j]=='b')

            {

                now[++top]=num[i][j];

            }

            else if(can[num[i][j]])

            {

                ans[i]=ans[i]+ans[num[i][j]];

                size[i]+=size[num[i][j]]-(len-1)*2;

                for(k=1;k<=len-1;k++) now[++top]=L[num[i][j]][k];

                ans[i]=ans[i]+BigNum(match(now+last+1,top-last));

                last=top;

                for(k=1;k<=len-1;k++) now[++top]=R[num[i][j]][k];

            }

            else if(!can[num[i][j]])

            {

                for(k=1;k<=size[num[i][j]];k++) now[++top]=L[num[i][j]][k];

            }

        }

        size[i]+=top;

        if(top>last) ans[i]=ans[i]+BigNum(match(now+last+1,top-last));

        if(size[i]>=len)

        {

            size[i]=len;

            can[i]=1;

            for(j=1;j<=len-1;j++)L[i][j]=now[j];

            for(j=1;j<=len-1;j++)R[i][j]=now[j+top-len+1];

        }

        else

        {

            for(j=1;j<=size[i];j++) L[i][j]=now[j];

        }

    }

    ans[n].print();

    return 0;

}

  

你可能感兴趣的:(KMP)