zoukankan      html  css  js  c++  java
  • 学习系列

    Manacher(马拉车)是一种求最长回文串的线性算法,复杂度O(n)。网上对其介绍的资料已经挺多了的,请善用搜索引擎。

    而扩展KMP说白了就是是求模式串和主串的每一个后缀的最长公共前缀【KMP更像是一个自动机】


    题目:

    POJ 1159: Palindrome

    求原字符串最少增加几个字符后可变成回文串,相当于求最长回文子序列的长度。

    解法:直接求串S和反转串Sr的最长公共子序列。

    #include <cstdlib>
    #include <cstdio>
    #include <fstream>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    #include <queue>
    
    #define rep(i, l, r) for(int i = l; i <= r; i++)
    #define down(i, l, r) for(int i = l; i >= r; i--)
    #define ll long long
    #define maxn 10009
    #define MAX 1<<30
    #define Q 1000000007
    
    using namespace std;
    
    int n, dp[2][5001];
    char s[5001];
    
    int main()
    {
        scanf("%d%s", &n, s);
        rep(i, 1, n) rep(j, 1, n) 
        {
            dp[i%2][j] = max(dp[(i-1)%2][j], dp[i%2][j-1]);
            if (s[i-1]==s[n-j] && dp[(i-1)%2][j-1]+1>dp[i%2][j]) dp[i%2][j] = dp[(i-1)%2][j-1]+1;
        }
        printf("%d
    ", n-dp[n%2][n]);
        return 0;
    }
    View Code

    HDU 3068: 最长回文

    模板题。

    #include <cstdlib>
    #include <cstdio>
    #include <fstream>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    #include <queue>
    
    #define rep(i, l, r) for(int i = l; i <= r; i++)
    #define down(i, l, r) for(int i = l; i >= r; i--)
    #define ll long long
    #define maxn 234567
    #define MAX 1<<30
    #define Q 1000000007
    
    using namespace std;
    
    int n, l, k[maxn], o, p, ans;
    char c[maxn], s[maxn];
    
    int main()
    {
        while (scanf("%s", c) == 1)
        {
            l = strlen(c);
            rep(i, 0, l-1) s[i*2+1] = '$', s[i*2+2] = c[i]; 
            l = l*2+1; s[l]=s[l+1] = '$'; s[0] = '#';
            o=p=ans=0;
            rep(i, 0, l)
            {
                k[i] = i<p ? min(k[2*o-i], p-i) : 1;
                while (s[i+k[i]] == s[i-k[i]]) k[i]++;
                if (i+k[i]>p) p = i+k[i], o = i;
                ans = max(ans, k[i]-1);
            }        
            printf("%d
    ", ans);
        }
        return 0;
    }
    View Code

    POJ 3974: Palindrome

    同模板题。

    #include <cstdlib>
    #include <cstdio>
    #include <fstream>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    #include <queue>
    
    #define rep(i, l, r) for(int i = l; i <= r; i++)
    #define down(i, l, r) for(int i = l; i >= r; i--)
    #define ll long long
    #define maxn 2000009
    #define MAX 1<<30
    #define Q 1000000007
    
    using namespace std;
    
    int t, n, l, k[maxn], o, p, ans;
    char c[maxn], s[maxn];
    
    int main()
    {
        while (++t)
        {
            scanf("%s", c);
            if (c[0]=='E') break; l = strlen(c);
            rep(i, 0, l-1) s[i*2+1]='$', s[i*2+2]=c[i];
            l=l*2+1; s[0]='#'; s[l]=s[l+1]='$';
            ans=o=p=0;
            rep(i, 1, l)
            {
                k[i] = i<p ? min(p-i, k[o*2-i]) : 1;
                while (s[i+k[i]] == s[i-k[i]]) k[i]++;
                if (i+k[i]>p) p=i+k[i], o=i;
                ans = max(ans, k[i]-1);
            }
            printf("Case %d: %d
    ", t, ans);
        }
        return 0;
    }
    View Code

    BZOJ 2342: 双倍回文

    求最长双倍回文。

    我实在是太弱了,于是写了个暴力+最优性剪枝,成功地用O(n^2)的算法AC!【其实正解是要O(nlogn)的算法的】

    #include <cstdlib>
    #include <cstdio>
    #include <fstream>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    #include <queue>
    
    #define rep(i, l, r) for(int i = l; i <= r; i++)
    #define down(i, l, r) for(int i = l; i >= r; i--)
    #define ll long long
    #define maxn 1000009
    #define MAX 1<<30
    #define Q 1000000007
    
    using namespace std;
    
    int t, n, l, k[maxn], o, p, ans, b[maxn];
    char c[maxn], s[maxn];
    
    int main()
    {
        scanf("%d%s", &l, c);
        rep(i, 0, l-1) s[i*2+1]='$', s[i*2+2]=c[i];
        s[0]='#'; s[l*2+1]='$';
        ans=o=p=0;
        rep(i, 1, l*2+1)
        {
            k[i] = i<p ? min(p-i, k[o*2-i]) : 1;
            while (s[i+k[i]] == s[i-k[i]]) k[i]++;
            if (i+k[i]>p) p=i+k[i], o=i;
        }
        rep(i, 1, l) b[i+1]=k[i*2-1]/2;
        rep(i, 1, l) 
            down(j, b[i]/2, ans+1) if (b[i-j]>=j && b[i+j]>=j) ans = j;
        printf("%d
    ", ans*4);
        return 0;
    }
    View Code

    BZOJ 2565: 最长双回文串

    求最长双回文串。

    对于每个字符,利用单调性质求出往左往右所能找到的最长的回文串。

    #include <cstdlib>
    #include <cstdio>
    #include <fstream>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    #include <queue>
    
    #define rep(i, l, r) for(int i = l; i <= r; i++)
    #define down(i, l, r) for(int i = l; i >= r; i--)
    #define ll long long
    #define maxn 200009
    #define MAX 1<<30
    #define Q 1000000007
    
    using namespace std;
    
    int t, n, l, k[maxn], o, p, ans, lt[maxn], rt[maxn];
    char c[maxn], s[maxn];
    
    int main()
    {
        scanf("%s", c); l=strlen(c);
        rep(i, 0, l-1) s[i*2+1]='$', s[i*2+2]=c[i];
        s[0]='#'; s[2*l+1]='$';
        o = p = 0;
        rep(i, 1, l*2+1)
        {
            k[i] = i<p ? min(p-i, k[2*o-i]) : 1;
            while (s[i+k[i]] == s[i-k[i]]) k[i]++;
            if (i+k[i]>p) p = i+k[i], o = i;
        }
        o = 1; rep(i, 2, l*2+1)
        {
            while (o+k[o]<=i) o++;
            lt[i] = i-o;
        }
        o = l*2+1; down(i, l*2, 1)
        {
            while (i<=o-k[o]) o--;
            rt[i] = o-i;
        }
        rep(i, 1, l*2+1) if (s[i]=='$' && lt[i] && rt[i] && ans < lt[i]+rt[i]) ans = lt[i]+rt[i];
        printf("%d
    ", ans);
        return 0;
    }
    View Code

    HDU 3613: Best Reward

    这题貌似EXKMP和Manacher都可以做。

    #include <cstdlib>
    #include <cstdio>
    #include <fstream>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    #include <queue>
    
    #define rep(i, l, r) for(int i = l; i <= r; i++)
    #define down(i, l, r) for(int i = l; i >= r; i--)
    #define ll long long
    #define maxn 500009
    #define MAX 1<<30
    #define Q 1000000007
    
    using namespace std;
    
    int tt, l, n[maxn], ex[maxn], v[maxn], t[26];
    char c[maxn], s[maxn];
    
    int main()
    {
        scanf("%d", &tt);
        while (tt--)
        {
            rep(i, 0, 25) scanf("%d", &t[i]);
            scanf("%s", s); int l = strlen(s);
            rep(i, 0, l-1) c[i] = s[l-i-1]; 
            int k, p, a; rep(i, 1, l-1) v[i] = 0;
            
            rep(i, 0, l-1) ex[i] = n[i] = 0;
            n[1] = 0; while (c[n[1]] == c[n[1]+1]) n[1]++;
            n[0] = l; k = 1; p = n[1];
            rep(i, 2, l-1)
            {
                n[i] = i<p ? min(n[i-k], l-i) : 0;
                while (n[i]+i<l && c[n[i]] == c[n[i]+i]) n[i]++;
                if (i+n[i]>p) p = i+n[i], k = i;
            }
            k = p = 0;
            rep(i, 0, l-1)
            {
                ex[i] = i<p ? min(n[i-k], p-i) : 0;
                while (ex[i]+i<l && c[ex[i]] == s[ex[i]+i]) ex[i]++;
                if (i+ex[i]>p) p = i+ex[i], k = i;
            }
            a = 0; rep(i, 1, l-1) { a+=t[s[l-i]-'a']; if (ex[l-i]==i) v[i]+=a; }
            
            rep(i, 0, l-1) ex[i] = n[i] = 0;
            n[1] = 0; while (s[n[1]] == s[n[1]+1]) n[1]++;
            n[0] = l; k = 1; p = n[1];
            rep(i, 2, l-1)
            {
                n[i] = i<p ? min(n[i-k], p-i) : 0;
                while (n[i]+i<l && s[n[i]] == s[n[i]+i]) n[i]++;
                if (i+n[i]>p) p = i+n[i], k = i;
            }
            k = p = 0;
            rep(i, 0, l-1)
            {
                ex[i] = i<p ? min(n[i-k], p-i) : 0;
                while (ex[i]+i<l && s[ex[i]] == c[ex[i]+i]) ex[i]++;
                if (i+ex[i]>p) p = i+ex[i], k = i;
            }
            a = 0; rep(i, 1, l-1) { a+=t[c[l-i]-'a']; if(ex[l-i]==i) v[l-i]+=a; }
            
            int ans = 0; rep(i, 1, l-1) ans = max(ans, v[i]); printf("%d
    ", ans);
        }
        return 0;
    }
    View Code

    HDU 4333: Revolving Digits

    扩展KMP的实例。。。

    【代码莫名写WA了】

    #include <cstdlib>
    #include <cstdio>
    #include <fstream>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    #include <queue>
    
    #define rep(i, l, r) for(int i = l; i <= r; i++)
    #define down(i, l, r) for(int i = l; i >= r; i--)
    #define ll long long
    #define maxn 300009
    #define MAX 1<<30
    #define Q 1000000007
    
    using namespace std;
    
    int tt, n[maxn];
    char s[maxn];
    
    int main()
    {
        scanf("%d", &tt); int t = 0;
        while (t++<tt)
        {
            scanf("%s", s); int l = strlen(s); 
            rep(i, 0, l-1) s[l+i] = s[i];
            int k, p;
            n[0] = l*2; n[1] = 0; while (s[n[1]] == s[n[1]+1]) n[1]++; p = n[k=1]+1;
            rep(i, 2, l-1) 
            {
                n[i] = i<p ? min(p-i, n[i-k]) : 0;
                while (s[n[i]] == s[n[i]+i]) n[i]++;
                if (i+n[i]>p) p = i+n[i], k = i;
            }
            int q=0, w=0, e=0;
            rep(i, 0, l-1) if (n[i]>=l) w++; else if (s[n[i]] < s[i+n[i]]) e++; else q++;
            printf("Case %d: %d %d %d
    ", t, q, w, e);
        }
        return 0;
    }
    View Code

    URAL 1297: Palindrome

    第三道模板题。

    #include <cstdlib>
    #include <cstdio>
    #include <fstream>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    #include <queue>
    
    #define rep(i, l, r) for(int i = l; i <= r; i++)
    #define down(i, l, r) for(int i = l; i >= r; i--)
    #define ll long long
    #define maxn 2009
    #define MAX 1<<30
    #define Q 1000000007
    
    using namespace std;
    
    int t, n, l, k[maxn], o, p, ans, b[maxn];
    char c[maxn], s[maxn];
    
    int main()
    {
        scanf("%s", c); l=strlen(c);
        rep(i, 0, l-1) s[i*2+1]='$', s[i*2+2]=c[i];
        s[0]='#'; s[2*l+1]='$';
        ans = o = p = 0;
        rep(i, 1, l*2+1)
        {
            k[i] = i<p ? min(p-i, k[2*o-i]) : 1;
            while (s[i+k[i]] == s[i-k[i]]) k[i]++;
            if (i+k[i]>p) p = i+k[i], o = i;
            if (k[ans]<k[i]) ans = i;
        }
        for(int i = ans-k[ans]+2; i <= ans+k[ans]-1; i+=2) printf("%c", s[i]);
        return 0;
    }
    View Code

    共8道题。

    总之,Manacher、KMP和ExKMP所利用的就是之前已经计算过的信息,以减少无意义的匹配。

  • 相关阅读:
    常用Dos 命令
    Expect: 100continue
    Sql Server 文件和文件组体系结构
    Build Action
    regasm.exe 程序集注册工具
    获得user account的SID,GUID
    sp_change_users_login
    Regsvr32
    ASP.NET IIS 注册工具 (Aspnet_regiis.exe)
    随机生成300道四则运算
  • 原文地址:https://www.cnblogs.com/NanoApe/p/4297236.html
Copyright © 2011-2022 走看看