zoukankan      html  css  js  c++  java
  • 马拉车manacher

    目的:线性查找一个串的最长回文子串

    时间复杂度:O(n)

    len[i]表示以i为中心的回文串的半径,mx即为当前计算回文串最右边字符的最大值,p是中心点mid,mx-i和2*p-1关于p对称

    https://blog.csdn.net/csdn_kou/article/details/82917937

    hdu3068,板子题,求最长回文长度。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=110000;
    int t,len[maxn*2];
    char S[maxn*2],T[maxn*2],s[maxn*2];
    
    int init(char *str)
    {
        int n=strlen(str);
        for(int i=1,j=0;i<=2*n;j++,i+=2)
        {
            s[i]='#';
            s[i+1]=str[j];
        }
        s[0]='$';
        s[2*n+1]='#';
        s[2*n+2]='@';
        s[2*n+3]='
    ';
        return 2*n+1; 
    } 
    void manacher(int n)
    {
        int mx=0,p=0;
        for(int i=1;i<=n;i++)
        {
            if(mx>i)len[i]=min(mx-i,len[2*p-i]);
            else len[i]=1;
            while(s[i-len[i]]==s[i+len[i]])len[i]++;
            if(len[i]+i>mx)mx=len[i]+i,p=i;
        }
    }
    
    int main()
    {
        while(scanf("%s",S)!=EOF)
        {
            int Len=strlen(S),n=init(S);
            for(int i=0;i<=n;i++)len[i]=0;
            manacher(n);
            
            int ans=0;
            for(int i=1;i<=n;i++)
            {
                ans=max(ans,len[i]-1);
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    View Code

    2019徐州G colorful string,求所有回文子串的value之和,一个串的value为串中字母种类,dfs预处理了第i位前一个a-z的位置,复杂度26*n。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=3e5+100;
    int t,len[maxn*2];
    char S[maxn*2],T[maxn*2],s[maxn*2];
    
    int init(char *str){
        int n=strlen(str);
        for(int i=1,j=0;i<=2*n;j++,i+=2){
            s[i]='#';
            s[i+1]=str[j];
        }
        s[0]='$';
        s[2*n+1]='#';
        s[2*n+2]='@';
        s[2*n+3]='
    ';
        return 2*n+1;
    }
    
    void manacher(int n)
    {
        int mx=0,p=0;
        for(int i=1;i<=n;i++){
            if(mx>i) len[i]=min(mx-i,len[2*p-i]);
            else len[i]=1;
            while(s[i-len[i]]==s[i+len[i]]) len[i]++;
            if(len[i]+i>mx) mx=len[i]+i,p=i;
        }
    }
    
    
    int dp[maxn*2][30],place[30];
    int main()
    {
        scanf("%s",S);
        int Len=strlen(S),n=init(S);
        for(int i=0;i<=n;i++)len[i]=0;
        manacher(n);
    
        ll ans=0;
        int k=0;
        for(int i=0;i<26;i++)place[i]=-1000000;
        for(int i=1;i<=n;i++)
        {
            if(i%2==0)place[S[k++]-'a']=i;
            for(int j=0;j<26;j++)dp[i][j]=place[j];
        }
    
    
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<26;j++)
            {
                if(i-dp[i][j]<len[i])
                {
                    ans+=1ll*(len[i]-(i-dp[i][j]))/2;
                }
            }
        }
        printf("%lld
    ",ans);
    
        return 0;
    }
    View Code

    hdu3613,一个串割成两个串,如果是回文串则val为所有字母val之和,否则为零。

    字母的val题目给出,求使总串的val最高的割法的val值。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=5e5+10;
    int t,len[maxn<<1];
    char S[maxn<<1],T[maxn<<1],s[maxn<<1];
    
    int init(char *str)
    {
        int n=strlen(str);
        for(int i=1,j=0;i<=2*n;j++,i+=2)
        {
            s[i]='#';
            s[i+1]=str[j];
        }
        s[0]='$';
        s[2*n+1]='#';
        s[2*n+2]='@';
        s[2*n+3]='
    ';
        return 2*n+1;
    }
    void manacher(int n)
    {
        int mx=0,p=0;
        for(int i=1;i<=n;i++)
        {
            if(mx>i)len[i]=min(mx-i,len[2*p-i]);
            else len[i]=1;
            while(s[i-len[i]]==s[i+len[i]])len[i]++;
            if(len[i]+i>mx)mx=len[i]+i,p=i;
        }
    }
    
    
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            int val[30]={0};
            for(int i=0;i<26;i++)scanf("%d",&val[i]); 
            cin>>S;
            int Len=strlen(S),n=init(S);
            for(int i=0;i<=n;i++)len[i]=0;
            manacher(n);
            
            int lg=0,rg=0,ans=0;
            for(int i=0;i<Len;i++)rg+=val[S[i]-'a'];
            for(int i=1;i<=n;i++)
            {
                if(i%2==0)
                {
                    lg+=val[s[i]-'a'];
                    rg-=val[s[i]-'a'];
                }
                else 
                {
                    int l=1,r=2*Len+1,tmplg=0,tmprg=0;
                    if(i!=l&&i!=r)
                    {
                        int mid1=(l+i)>>1,mid2=(i+r)>>1;
                        if(len[mid1]==mid1-l+1)tmplg=lg;
                        if(len[mid2]==r-mid2+1)tmprg=rg;
                        ans=max(ans,tmplg+tmprg);
                    }
                }
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    View Code

    hdu3294,求最早出现的最长回文串的l、r区间,并且转换后输出

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e5+10;
    int t,len[maxn<<1];
    char S[maxn<<1],T[maxn<<1],s[maxn<<1];
    int init(char *str)
    {
        int n=strlen(str);
        for(int i=1,j=0;i<=2*n;j++,i+=2)
        {
            s[i]='#';
            s[i+1]=str[j];
        }
        s[0]='$';
        s[2*n+1]='#';
        s[2*n+2]='@';
        s[2*n+3]='
    ';
        return 2*n+1;
    }
    void manacher(int n)
    {
        int mx=0,p=0;
        for(int i=1;i<=n;i++)
        {
            if(mx>i)len[i]=min(mx-i,len[2*p-i]);
            else len[i]=1;
            while(s[i-len[i]]==s[i+len[i]])len[i]++;
            if(len[i]+i>mx)mx=len[i]+i,p=i;
        }
    }
    
    int main()
    {
        char ch;
        while(scanf("%c %s",&ch,S)!=EOF)
        {
            int Len=strlen(S),n=init(S);
            for(int i=0;i<=n;i++)len[i]=0;
            manacher(n);
    
            int ans=0,l=0,r=0;
            for(int i=1;i<=n;i++)
            {
                if(len[i]-1>ans)
                {
                    ans=len[i]-1;
                    l=(i-len[i]+1)/2;
                    r=(i+len[i]-3)/2;
                }
            }
            if(ans<2)printf("No solution!
    ");
            else
            {
                printf("%d %d
    ",l,r);
                for(int i=l;i<=r;i++)
                {
                    S[i]=S[i]-ch+'a';
                    if(S[i]>'z')S[i]=S[i]-'z'+'a'-1;
                    if(S[i]<'a')S[i]=S[i]+'z'-'a'+1;
                }
                S[r+1]='';
                printf("%s
    ",S+l);
            }
            getchar();
        }
        return 0;
    }
    View Code

    hdu4513,最长递增(不降)回文串,manacher里只需要改个while

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e5+10;
    int t,len[maxn*2];
    int s[maxn*2],T[maxn*2],S[maxn*2];
    
    int init(int *str,int n)
    {
        for(int i=1,j=0;i<=2*n;j++,i+=2)
        {
            s[i]=-1;
            s[i+1]=str[j];
        }
        s[0]=-2;
        s[2*n+1]=-1;
        s[2*n+2]=-3;
        return 2*n+1;
    }
    void manacher(int n)
    {
        int mx=0,p=0;
        for(int i=1;i<=n;i++)
        {
            if(mx>i)len[i]=min(mx-i,len[2*p-i]);
            else len[i]=1;
            while(s[i-len[i]]==s[i+len[i]]&&s[i-len[i]]<=s[i-len[i]+2])len[i]++;
            if(len[i]+i>mx)mx=len[i]+i,p=i;
        }
    }
    
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            int n;
            scanf("%d",&n);
            for(int i=0;i<n;i++)
            scanf("%d",&S[i]);
            int Len=n,len2=init(S,n);
            for(int i=0;i<=n;i++)len[i]=0;
            manacher(len2);
            
            int ans=0;
            for(int i=1;i<=len2;i++)
            {
                ans=max(ans,len[i]-1); 
            } 
            printf("%d
    ",ans);
        }
        return 0;
    }
    View Code

    ... 

  • 相关阅读:
    XP系统开机慢和减少开机滚动条的解决方法
    细说A标签的样式
    【部分转载】安装MySQL,visual studio tools for office 2010 runtime通不过
    将GB2312转码成UTF8
    ubuntu/vmware 拷贝后网路不通
    bash manual
    跟我一起写 Makefile
    我的vi 启动配置文件
    ubuntu安装samba
    upnp
  • 原文地址:https://www.cnblogs.com/myrtle/p/11492707.html
Copyright © 2011-2022 走看看