马拉车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]='\n';
    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\n",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]='\n';
    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\n",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]='\n';
    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\n",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]='\n';
    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!\n");
        else
        {
            printf("%d %d\n",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]='\0';
            printf("%s\n",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\n",ans);
    }
    return 0;
}
View Code

 

... 

转载于:https://www.cnblogs.com/myrtle/p/11492707.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值