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所利用的就是之前已经计算过的信息,以减少无意义的匹配。

  • 相关阅读:
    Building a flexiable renderer
    Indirect Illumination in mental ray
    我的心情
    Cellular Automata
    Subsurface Scattering in mental ray
    Shader Types in mental ray
    BSP Traversal
    我的渲染器终于达到了MR的速度
    How to handle displacement and motion blur
    说明
  • 原文地址:https://www.cnblogs.com/NanoApe/p/4297236.html
Copyright © 2011-2022 走看看