zoukankan      html  css  js  c++  java
  • POJ 3376 Finding Palindromes (tire树+扩展kmp)

    很不错的一个题(注意string会超时)

    题意:给你n串字符串,问你两两匹配形成n*n串字符串中有多少个回文串

    题解:我们首先需要想到多串字符串存储需要trie树(关键),然后我们正序插入倒序匹配就可以O(len)找到回文串个数了。

    但是如果每次直接查询到结尾的话会漏掉两种情况

    如:  1:a 与 ba 匹配(使用的是ab去匹配a)

            2:ab 与 a 匹配

    对于2这种情况我们需要在trie树记录每个字符串每个后缀可以形成回文串的个数(建树时就维护),对于1我们则需在匹配时看查询串每个后缀是否形成回文串。

    扩展KMP:对于字符串str1的每一位与str2的最长前缀匹配记为ntand。

    首先求得str1对于自己的每一位的“ntand”记为nnext,接着根据nnext求得ntand(两个函数想法与写法几乎一致)。

    求nnext:我们首先暴力匹配第1个,接着根据字符串那面求得的匹配信息无回溯的求下一位

    至于寻找每个后缀是否形成回文串我们使用扩展KMP的字符串正序和倒序的匹配长度来计算。

    具体看代码

    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<vector>
    #include<string>
    #include<cstdio>
    #include<cstring>
    #include<stdlib.h>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define eps 1E-8
    /*注意可能会有输出-0.000*/
    #define Sgn(x) (x<-eps? -1 :x<eps? 0:1)//x为两个浮点数差的比较,注意返回整型
    #define Cvs(x) (x > 0.0 ? x+eps : x-eps)//浮点数转化
    #define zero(x) (((x)>0?(x):-(x))<eps)//判断是否等于0
    #define mul(a,b) (a<<b)
    #define dir(a,b) (a>>b)
    typedef long long ll;
    typedef unsigned long long ull;
    const int Inf=1<<28;
    const double Pi=acos(-1.0);
    const int Mod=1e9+7;
    const int Max=2100000;
    struct node
    {
        int next[26];
        int coun,npd;//此处结束的单词个数 接下来是回文的单词个数
        void init()
        {
            memset(next,0,sizeof(next));
            coun=npd=0;
        }
    } trie[Max];
    int nnext[Max],ntand[Max];
    void Exnext(int len,char *str)//扩展KMP求next数组:是数组此对应位置与开头的最长公共前缀
    {
        nnext[0]=len;
        nnext[1]=0;
        for(int i=0; i+1<len&&str[i]==str[i+1]; ++i) //暴力匹配第二位
            nnext[1]++;
        int k=1;//最远匹配的下标
        int p,l,j;
        for(int i=2; i<len; ++i)
        {
            p=k+nnext[k]-1;
            l=nnext[i-k];
            if(i+l<=p)//可以画图来看,一定是匹配的
                nnext[i]=l;
            else
            {
                j=p-i+1;//不确定的位置
                if(j<0)
                    j=0;
                while(i+j<len&&str[j]==str[i+j])//可以无回溯的暴力匹配
                    ++j;
                nnext[i]=j;
                k=i;
            }
        }
        return;
    }
    void Extand(int len,char *str1,char *str2)//两字符串的最长公共前缀
    {
        Exnext(len,str1);//正序的每一位和倒序相匹配 求辅助数组
        ntand[0]=0;//之后与辅助数组一样
        for(int i=0; i<len&&str1[i]==str2[i]; ++i)
            ntand[0]++;
        int k=0;
        int l,p,j;
        for(int i=1; i<len; ++i)
        {
            p=k+ntand[k]-1;
            l=nnext[i-k];
            if(i+l<=p)
                ntand[i]=l;
            else
            {
                j=p-i+1;
                if(j<0)
                    j=0;
                while(i+j<len&&str1[j]==str2[i+j])
                    ++j;
                ntand[i]=j;
                k=i;
            }
        }
        return ;
    }
    char str1[Max],str2[Max];
    int len[Max],tot;
    void Insert(int j,char *str)//trie树插入
    {
        int now=0,mpos;
        for(int i=0; i<len[j]; ++i)
        {
            mpos=str[i]-'a';
            if(!trie[now].next[mpos])
            {
                trie[now].next[mpos]=++tot;
                trie[tot].init();
            }
            now=trie[now].next[mpos];
            if(i<len[j]-1&&ntand[i+1]==len[j]-i-1)//此位置之后是回文串
            {
                 trie[now].npd++;
            }
    
        }
        trie[now].coun++;
        return;
    }
    char str[Max];
    ll Search(int j)
    {
        ll ans=0ll;
        int now=0,mpos;
        for(int i=0; i<len[j]; ++i)
        {
            mpos=str2[i]-'a';//倒序
            if(!trie[now].next[mpos])
                return ans;
            now=trie[now].next[mpos];
            if(i<len[j]-1&&ntand[i+1]==len[j]-i-1)//此位置之后是回文串
                ans+=(ll)trie[now].coun;
        }
        return ans+(ll)trie[now].coun+(ll)trie[now].npd;//匹配结束
    }
    int main()
    {
        int n;
        while(~scanf("%d",&n))
        {
            tot=0;
            trie[tot].init();
            int sum=0;
            for(int i=0; i<n; ++i)
            {
                scanf("%d %s",&len[i],str+sum);
                int coun=0;
                for(int j=len[i]-1;j>=0;--j) //倒序
                    str2[coun++]=str[j+sum];
                for(int j=0; j<len[i]; ++j)
                    str1[j]=str[j+sum];
                str1[len[i]]=str2[len[i]]='';
                Extand(len[i],str2,str1);
                Insert(i,str1);//正序来插入
                sum+=len[i];
            }
            ll ans=0ll;
            sum=0;
            for(int i=0; i<n; ++i)
            {
                for(int j=0; j<len[i]; ++j)
                {
                    str1[j]=str[j+sum];
                    str2[j]=str[len[i]-j-1+sum];
                }
                str1[len[i]]=str2[len[i]]='';
                Extand(len[i],str1,str2);
                ans+=Search(i);//倒序查询
                sum+=len[i];
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    android之APN
    Simple XML
    Retrofit – Java(Android) 的REST 接口封装类库
    Android 删除短信
    解决android:background背景图片被拉伸问题
    人分三等,你是哪一等?
    将android中的sample例子到eclipse中
    linux内存管理
    Android 使用android-support-multidex解决Dex超出方法数的限制问题,让你的应用不再爆棚(转)
    使用maven创建web项目
  • 原文地址:https://www.cnblogs.com/zhuanzhuruyi/p/5877597.html
Copyright © 2011-2022 走看看