zoukankan      html  css  js  c++  java
  • 最长回文子串

    有这样一种题,求一个字符串的最长回文子串的长度。

    hdu 3608就是求最长回文子串的一道题

    有这样几种解法

    1.暴力法

    枚举所有子串且每次判断该子串是否是回文子串,时间复杂度是n3,理所当然的T掉。

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    const int maxa = 110000;
    char str[maxa];
    int main(){
        while(scanf("%s", &str)!=EOF){
            int ans = 0;
            for(int i = 0; str[i]; i++){
                for(int k = i+1; str[k]; k++){
                    int ok = 1;
                    for(int j = i; j <= (i+k)/2; j++){
                        if(str[j] != str[k-(j-i)]){
                            ok = 0;
                            break;
                        }
                    }
                    if(ok){
                        ans = max(ans, k-i+1);
                    }
                }
            }
            printf("%d
    ", ans);
        }
    }
    View Code

    2.枚举中心法

    首先将字符串每个字符的两遍都加上特殊字符,变成新串,例如原串为abab,新串为#a#b#a#b#,然后以每个点为中心,求出以该点为中心的最长回文串的长度,枚举中心的时间复杂度是n,枚举最长长度的时间复杂度是n,总时间复杂度是n2.依旧超时。

    /*************************************************************************
        > File Name: hdu3680.cpp
        > Author: ma6174
        > Mail: ma6174@163.com
        > Created Time: Sat 26 Dec 2015 03:35:08 AM PST
     ************************************************************************/
    #include<iostream>
    #include<string.h>
    #include<stdio.h>
    using namespace std;
    const int maxa = 111111;
    char a[maxa];
    char str[maxa*2];
    int main(){
        while(scanf("%s", &a)!=EOF){
            int ans = 0;
            for(int i = 0; ; i++){
                if(a[i]){
                    str[i*2] = '#';
                    str[i*2+1] = a[i];
                }else{
                    str[i*2] = '#';
                    str[i*2+1] = 0;
                    break;
                }
            }
            printf("%s
    ", str);
            int len = strlen(str);
            for(int i = 0; str[i]; i++){
                for(int j = 0; i-j >= 0 && i+j < len; j++){
                    if(str[i-j] == str[i+j]){
                        ans = max(ans, j);
                    }else break;
                }
            }
            printf("%d
    ", ans);
        }
    }
    View Code

    3.动态规划

    for循环枚举所有子串,当str(i,j)被枚举到的时候,str(i+1,j-1)也一定被枚举到,dp[i][j] =1代表str(i,j)是回文子串,只要str[i] == str[j]并且,dp[i+1][j-1]==1,那么dp[i][j] = 1,时间复杂度为n2,空间复杂度也是n2.

    /*************************************************************************
        > File Name: hdu3680.cpp
        > Author: ma6174
        > Mail: ma6174@163.com
        > Created Time: Sat 26 Dec 2015 03:35:08 AM PST
     ************************************************************************/
    #include<iostream>
    #include<string.h>
    #include<stdio.h>
    using namespace std;
    const int maxa = 1111;
    char str[maxa];
    int dp[maxa][maxa];
    int main(){
        while(scanf("%s", &str)!=EOF){
            memset(dp, 0, sizeof(dp));
            int l = strlen(str);
            for(int i = 0; i < l; i++){
                dp[i][i] = 1;
            }
            int ans = 0;
            for(int j = 1; j < l; j++){
    
                for(int i = 0; str[i]; i++){
                    if(i+j > l) break;
                    int k = i+j;
                    if(str[i] == str[k]){
                        if(i+1 > k-1 || dp[i+1][k-1]){
                            dp[i][k] = 1;
                            ans = max(ans, k-i+1);
                        }
                    }
                }
            }
            printf("%d
    ", ans);
        }
    }
    View Code

    4.namacher算法

    将字符串中的每个字符的左右都加上一个串中不会出现的特殊字符,例如字符串为abcde,那么变换后的串为#a#b#c#d#e#

    我们称变换后的串为str2

    另p[i]为str2中第i个字符为中心的最长回文串的边界到中心的距离,i加上p[i],就是以i为中心的最长回文子串的右边界,用两个辅助变量分别是id,和mx。mx代表之前所有回文串最右右边界,id代表那最右右边界的点。

    如上图所示i’代表i在id的对应左侧对应位置,如果以i为中心的回文串的边界不超过以id为中心的回文串的范围的话那么p[i] == p[i'],如果超过的话p[i] == 2*id-i,接下来在以为以为扩张p[i]。

    由于每次扩张都会导致mx变大,而mx最大不会超过strlen(str2),所以时间复杂度为n。

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    const int maxa = 111111*2;
    int p[maxa*2];
    #define max(a,b) a>b?a:b;
    #define min(a,b) a<b?a:b;
    int rebuild(int n, char* a, char* str){
        for(int i = 0 ;i < n; i++){
            a[i*2+1] = '#';
            a[i*2+2] = str[i];
        }
        a[2*n+1] = '#';
        a[2*n+2] = 0;
        a[0] = '$';
        return 2*n+2;
    }
    int manachar(int n, char* a){
        int mx = 0, id = 0;
        int ans = 0;
       // printf("%s
    ", a);
        for(int i = 0; i < n; i++){
            if(mx > p[i]){
                int i2 = 2*id - i;
                p[i] = min(mx - i, p[i2]);
            }else p[i] = 0;
            while(a[i-p[i]-1] == a[i+p[i]+1]){
                p[i]++;
            }
            ans = max(ans, p[i]);
            if(p[i] +i > mx){
                id = i;
                mx = p[i]+i;
            }
        }
        return ans;
    }
    
    char str[maxa*2];
    char a[maxa*2];
    
    int main(){
        while(scanf("%s", &str)!=EOF){
            int n = strlen(str);
            n = rebuild(n,a, str);
           // printf("fuck");
            printf("%d
    ", manachar(n, a));
        }
    }
    View Code

    5.后缀数组

    将字符串倒置接在原来字符串后,两个字符串之间用特殊字符分割,例如原串为abc,新串即为abc#cba。

    我们将新串的前半部分称为str1,后半部分称为str2。

    此时分两种情况,一种是求最长奇回文串,一种是求最长偶回文串。

    (1)求最长奇回文串。枚举以每个字符为中心时的奇回文长度,只要求出第i个字符在str2中的位置,在求出两点的rank,然后求出两点rank之间的最小height,长度乘2-1就是以此点为中心的最长奇回文的长度。

    (2)求最长偶回文,求出某一节点在str2中对应的位置的下一个节点,同(1)步骤求出的值*2即为以此点为中心右侧的偶回文的长度。

     时间复杂度是nlogn。

    #include<iostream>
    #include<string.h>
    #include<stdio.h>
    using namespace std;
    
    #define rep(i,n) for(int i = 0;i < n; i++)
    using namespace std;
    const int size  = 222222,INF = 1<<30;
    int rk[size],sa[size],height[size],w[size],wa[size],res[size];
    void getSa (int len,int up) {
        int *k = rk,*id = height,*r = res, *cnt = wa;
        rep(i,up) cnt[i] = 0;
        rep(i,len) cnt[k[i] = w[i]]++;
        rep(i,up) cnt[i+1] += cnt[i];
        for(int i = len - 1; i >= 0; i--) {
            sa[--cnt[k[i]]] = i;
        }
        int d = 1,p = 0;
        while(p < len){
            for(int i = len - d; i < len; i++) id[p++] = i;
            rep(i,len)    if(sa[i] >= d) id[p++] = sa[i] - d;
            rep(i,len) r[i] = k[id[i]];
            rep(i,up) cnt[i] = 0;
            rep(i,len) cnt[r[i]]++;
            rep(i,up) cnt[i+1] += cnt[i];
            for(int i = len - 1; i >= 0; i--) {
                sa[--cnt[r[i]]] = id[i];
            }
            swap(k,r);
            p = 0;
            k[sa[0]] = p++;
            rep(i,len-1) {
                if(sa[i]+d < len && sa[i+1]+d <len &&r[sa[i]] == r[sa[i+1]]&& r[sa[i]+d] == r[sa[i+1]+d])
                    k[sa[i+1]] = p - 1;
                else k[sa[i+1]] = p++;
            }
            if(p >= len) return ;
            d *= 2,up = p, p = 0;
        }
    }
    void getHeight(int len) {
        rep(i,len) rk[sa[i]] = i;
        height[0] =  0;
        for(int i = 0,p = 0; i < len - 1; i++) {
            int j = sa[rk[i]-1];
            while(i+p < len&& j+p < len&& w[i+p] == w[j+p]) {
                p++;
            }
            height[rk[i]] = p;
            p = max(0,p - 1);
        }
    }
    int getSuffix(char s[]) {
        int len = strlen(s),up = 0;
        for(int i = 0; i < len; i++) {
            w[i] = s[i];
            up = max(up,w[i]);
        }
        w[len++] = 0;
        getSa(len,up+1);
        getHeight(len);
        return len;
    }const int maxa = 222222;
    char str[maxa];
    int rmp[maxa][32];
    int log(int n){
        int cnt = 0;
        while(n){
            cnt ++;
            n /= 2;
        }
        return cnt - 1;
    }
    int RMQ(int n){
        for(int i = 0;i < n; i++){
            rmp[i][0] = height[i];
        }
        int l = log(n);
        for(int i = 1; i < l; i++){
            for(int j = 0; j+(1<<(i-1)) < n; j++){
                rmp[j][i] = min(rmp[j][i-1], rmp[j+(1<<(i-1))][i-1]);
            }
        }
    }
    int r1r2(int a, int b){
        int j = log(b-a+1);
        return min(rmp[a][j], rmp[b-(1<<j)+1][j]);
    
    }
    int main(){
        while(scanf("%s", str)!=EOF){
            int l = strlen(str);
            str[l] = '#';
            for(int i = 0; i < l; i++){
                str[l+1+i] = str[l-1-i];
            }
            str[2*l+1] = 0;
            //printf("%s
    ", str);
            getSuffix(str);
            int n = strlen(str);
            RMQ(n);
            int ans = 0;
            for(int i= 0; str[i] != '#'; i++){
                int i1 =  2*l-i;
                //printf("%d == i %d == i1
    ", i, i1);
               // printf("%d %d
    ", rk[i], rk[i1]);
                int a = rk[i], b = rk[i1];
               // printf("%d ", 2*r1r2(min(a, b) + 1, max(a, b))-1);
                ans = max(ans, 2*r1r2(min(a, b) + 1, max(a, b))-1);
                i1++;
                a = rk[i], b = rk[i1];
                ans = max(ans, 2*r1r2(min(a, b) + 1, max(a, b)));
               // printf("%d
    ", 2*r1r2(min(a, b) + 1, max(a, b)));
            }
            printf("%d
    ", ans);
        }
    }
    View Code
  • 相关阅读:
    JS条件判断小技巧
    简单实现节流函数和防抖函数(转载)
    一篇常做错的经典JS闭包面试题
    前端冷知识集结
    闭包
    仔细认识setInterval
    仔细认识setTimeout
    单方向指定时间内的匀速运动
    图片延迟加载
    优化网页上的gif
  • 原文地址:https://www.cnblogs.com/icodefive/p/5072741.html
Copyright © 2011-2022 走看看