求迴文串(Manacher算法)~算法競賽入門-基礎篇

最長迴文

Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 7448 Accepted Submission(s): 2541


Problem Description
給出一個只由小寫英文字符a,b,c...y,z組成的字符串S,求S中最長迴文串的長度.
迴文就是正反讀都是一樣的字符串,如aba, abba等

Input
輸入有多組case,不超過120組,每組輸入爲一行小寫英文字符a,b,c...y,z組成的字符串S
兩組case之間由空行隔開(該空行不用處理)
字符串長度len <= 110000

Output
每一行一個整數x,對應一組case,表示該組case的字符串中所包含的最長迴文長度.

Sample Input
aaaa abab

Sample Output
4 3
做了一個超時的普通的算法(需要去求基數迴文和偶數迴文)
#include<stdio.h>
#include<string.h>
#define MAX 110000
#define INF 10000000
int main(){
    char st[MAX];
    while(scanf("%s",st)!=EOF){
        int markl=INF,markr=-1,mid,right=-1,left=INF;
        int i,j;
        int flag=0;//標誌在字符串中是否有迴文串,有迴文串,flag爲1
        int maxlen=-INF;//初始化迴文串的最大長度
        int n=strlen(st);
        int t=n;
        mid=n/2;
        /**從字符串的右邊查找回文串*/
        for(i=mid;i<t;i++){
            for(j=0;j<2;j++){
                right=(n%2==0)?i:i+1;//是奇數則右+1
                left=i-1;
                n--;
                while(left>=0&&right<t){
                    /**左右相等字符,說明有迴文*/
                     if(st[left]==st[right]){
                        left--,right++;
                        flag=1;
                     }
                     else
                        break;
                }
                if(maxlen<(right-1)-(left+1)+1){
                    /**跳出上面的循環時,left的值是停留在
                    我要記錄的有相等的字符的位置的左邊所
                    以我呀在後面記錄的時候加1,right也是
                    同理得到*/
                    markl=left+1;
                    markr=right-1;
                    maxlen=(markr-markl)+1;
                }
            }
            n=n+1;
        }
        n=t-1;
        /**從字符串的左邊查找回文串*/
        for(i=mid-1;i>=0;i--){
            for(j=0;j<2;j++){
                right=(n%2==0)?i:i+1;//是奇數則右+1
                left=i-1;
                n--;
                while(left>=0&&right<t){
                    /**左右相等字符,說明有迴文*/
                     if(st[left]==st[right]){
                        left--,right++;
                        flag=1;
                     }
                     else
                        break;
                }
                if(maxlen<(right-1)-(left+1)+1){
                    /**跳出上面的循環時,left的值是停留在
                    我要記錄的有相等的字符的位置的左邊所
                    以我呀在後面記錄的時候加1,right也是
                    同理得到*/
                    markl=left+1;
                    markr=right-1;
                    maxlen=(markr-markl)+1;
                }
            }
            n=n+1;
        }
        /**flag爲1,則串中有迴文*/
        if(flag){
            printf("%d\n",maxlen);
        }
        else
          printf("1\n");
    }
    return 0;
}
/**做一個未超時不需要比較奇數迴文和偶數迴文的算法,它叫Manacher算法*
<pre name="code" class="cpp">#include<stdio.h>
#include<string.h>
#define MAX 100010*3
char  s[MAX];
char  st[MAX];
int p[MAX];
int n;
void transferto(){
    n=strlen(s);
    st[0]='$';
    st[1]='#';
    int i;
    for(i=0;i<n;i++){
        st[i*2+2]=s[i];
        st[i*2+3]='#';
    }
    n=2*n+2;
    st[n]='\0';
}
/**關鍵算法*/
void geth(){
    int i;
    int mx = 0;
    int id;
    for(i=1; i<n; i++){
        if(mx>i)
            p[i]=(mx-i>p[id*2-i])?p[id*2-i]:mx-i;
        else
            p[i]=1;
        for(;st[i+p[i]] == st[i-p[i]]; p[i]++);
        if(p[i]+i>mx){
            mx=p[i]+i;
            id=i;
        }
    }
}
void getm(){
    int i;
    int max=-MAX;
    for(i=1;i<n;i++){
        if(max<p[i]){
             max=p[i];
        }
    }
    printf("%d\n",max-1);
}
int main(){
    while(scanf("%s",s)!=EOF){
        transferto();
        geth();
        getm();
    }
    return 0;
}


分析:其實左邊的p[j]在之前已經求得,而現在的左邊和右邊是相等的,所以p[i]和p[j]的迴文數是相等的


發佈了35 篇原創文章 · 獲贊 0 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章