zoukankan      html  css  js  c++  java
  • HDU3613 Manacher//EXKMP//KMP

    http://acm.hdu.edu.cn/showproblem.php?pid=3613

    每个字符都有一个权值,将一个字符串分成两半,如果某一半是回文串就把所有的字符权值加起来,否则当0来处理,问最大值会是多少。

    这题很明显是判断前后缀的回文串然后用O(n)的时间遍历取最大值。

    问题在于如何判断是前后缀的最大回文串,对于回文串,就很自然而然的想到Manacher算法,对于每一个点,如果以它为中心的回文字符串和最前端接上,他就是一个前缀的回文串,如果和后面接上就是一个后缀的回文串,将所有的这些信息加入到一个数组中之后询问即可。

    #include <map>
    #include <set>
    #include <ctime>
    #include <cmath>
    #include <queue>
    #include <stack>
    #include <vector>
    #include <string>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <sstream>
    #include <iostream>
    #include <algorithm>
    #include <functional>
    using namespace std;
    #define For(i, x, y) for(int i=x;i<=y;i++)  
    #define _For(i, x, y) for(int i=x;i>=y;i--)
    #define Mem(f, x) memset(f,x,sizeof(f))  
    #define Sca(x) scanf("%d", &x)
    #define Scl(x) scanf("%lld",&x);  
    #define Pri(x) printf("%d
    ", x)
    #define Prl(x) printf("%lld
    ",x);  
    #define LL long long
    #define ULL unsigned long long  
    const int maxn = 5e5 + 10;
    const int INF = 0x3f3f3f3f;
    int N,M,tmp,K; 
    int value[30];
    char str[maxn];
    char Ma[maxn * 2];
    int Mp[maxn * 2];
    bool cut[maxn][2];
    LL sum[maxn];
    void Manacher(char *x,int n){
        int l = 0;
        Ma[l++] = '$'; Ma[l++] = '#';
        for(int i = 0 ; i < n ; i ++){
            Ma[l++] = x[i];
            Ma[l++] = '#';
        }
        int mx = 0,id = 0;
        for(int i = 0 ; i < l ; i ++){
            Mp[i] = mx > i?min(mx - i,Mp[2 * id - i]):1;
            while(i + Mp[i] < l && Ma[i + Mp[i]] == Ma[i - Mp[i]]) Mp[i]++;
            if(Mp[i] + i > mx){
                mx = Mp[i] + i;
                id = i;
            }
        }
    }
    int main()
    {
        int T; scanf("%d",&T);
        while(T--){
            Mem(cut,0);
            For(i,0,25) Sca(value[i]);
            scanf("%s",str);
            int len = strlen(str);
            Manacher(str,len);
            sum[0] = 0;
            for(int i = 1; i <= len; i ++) sum[i] = sum[i - 1] + value[str[i - 1] - 'a'];
            for(int i = 2 ; i < len * 2 + 1; i ++){
                if(i - Mp[i] == 0) cut[Mp[i] - 1][0] = 1;
                if(i + Mp[i] == len + len + 2) cut[Mp[i] - 1][1] = 1;
            }
            LL ans = -99999999;
            For(i,1,len - 1){
                LL s = 0;
                if(cut[i][0]) s += sum[i];
                if(cut[len - i][1]) s += sum[len] - sum[i];
                ans = max(ans,s);
            }
            Prl(ans);
        }
        return 0;
    }
    View Code

    当我们想到解决回文串问题的时候将字符串反转,我们就可以考虑用到扩展KMP算法了。

    将原字符串s1进行反转之后得到的s2,当s1作为模式串,s2作为主串进行匹配时,得到的是s2的所有后缀与s1的前缀的最大匹配值,也就是说明当i == extend1[l - i]的时候,s1的前i个字符和s1的后i个字符相等,也就是说前i个字符是s的回文前缀。

    同理,当s2作为模式串与s1匹配的时候,得到的是s1的所有后缀与s2的前缀的最大匹配值,当extend2[] == l - i 的时候就是s的回文后缀。

    #include <map>
    #include <set>
    #include <ctime>
    #include <cmath>
    #include <queue>
    #include <stack>
    #include <vector>
    #include <string>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <sstream>
    #include <iostream>
    #include <algorithm>
    #include <functional>
    using namespace std;
    #define For(i, x, y) for(int i=x;i<=y;i++)  
    #define _For(i, x, y) for(int i=x;i>=y;i--)
    #define Mem(f, x) memset(f,x,sizeof(f))  
    #define Sca(x) scanf("%d", &x)
    #define Scl(x) scanf("%lld",&x);  
    #define Pri(x) printf("%d
    ", x)
    #define Prl(x) printf("%lld
    ",x);  
    #define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
    #define LL long long
    #define ULL unsigned long long  
    #define mp make_pair
    #define PII pair<int,int>
    #define PIL pair<int,long long>
    #define PLL pair<long long,long long>
    #define pb push_back
    #define fi first
    #define se second 
    #define Vec Point
    typedef vector<int> VI;
    const double eps = 1e-9;
    const int maxn = 5e5 + 10;
    const int INF = 0x3f3f3f3f;
    const int mod = 1e9 + 7; 
    int N,M,tmp,K; 
    int value[30];
    char s1[maxn];
    char s2[maxn];
    int next1[maxn],next2[maxn];
    int extend1[maxn],extend2[maxn];
    bool cut[maxn][2];
    LL sum[maxn];
    void EKMP_Pre(char x[],int m,int *next){
        int j = 0;
        next[0] = m;
        while(j + 1 < m && x[j] == x[j + 1]) j ++;
        next[1] = j;
        int k = 1;
        for(int i = 2; i < m ; i++){
            int p = next[k] + k - 1;
            int l = next[i - k];
            if(i + l - 1 < p){
                next[i] = l;
            }else{
                int j = max(0,p - i + 1);
                while(i + j < m && x[i + j] == x[j]) j ++;
                k = i;
                next[i] = j;
            }
        }
    }
    void EKMP(char x[],int m,char y[],int n,int *next,int *extend){
        EKMP_Pre(x,m,next);
        int j = 0;
        while(x[j] == y[j]) j ++;
        extend[0] = j;
        int k = 0;
        for(int i = 1 ; i < n ; i ++){
            int p = extend[k] + k - 1;
            int l = next[i - k];
            if(l + i < p + 1) extend[i] = l;
            else{
                int j = max(0,p - i + 1);
                while(i + j < n && j < m && y[i + j] == x[j]) j++;
                k = i;
                extend[i] = j;
            }
        }
    }
    int main()
    {
        int T; Sca(T);
        while(T--){
            Mem(cut,0);
            For(i,0,25) Sca(value[i]);
            scanf("%s",s1); int l = strlen(s1);
            for(int i = 0 ; i < l ; i ++) s2[l - i - 1] = s1[i];
            EKMP(s1,l,s2,l,next1,extend1);    
            EKMP(s2,l,s1,l,next2,extend2);   
            for(int i = 1; i <= l ; i ++) sum[i] = sum[i - 1] + value[s1[i - 1] - 'a'];
            LL ans = 0;
            for(int i = 1; i < l; i ++){
                LL s = 0;
                if(extend1[l - i] == i) s += sum[i];
                if(extend2[i] == l - i) s += sum[l] - sum[i];
                ans = max(ans,s);
            }
            Prl(ans);
        }
        #ifdef VSCode
        system("pause");
        #endif
        return 0;
    }
    View Code

    虽然KMP算法是比较基础的算法,但是本题kmp算法在我看来是最难想到的。

    与EXKMP一样,将字符串反转形成s1,s2之后,用s1作为模式串与s2匹配,当匹配完成后,指向s2的指针一定是处于末尾的,此时指向s1的指针就是与s2的最大前缀匹配,依据next的特性,我们将前缀k往前跳的每一个点也必然是s1的前缀和s2的后缀匹配的点,也就是s的回文前缀长度。

    同理,我们处理出s的回文后缀长度即可。

    #include <map>
    #include <set>
    #include <ctime>
    #include <cmath>
    #include <queue>
    #include <stack>
    #include <vector>
    #include <string>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <sstream>
    #include <iostream>
    #include <algorithm>
    #include <functional>
    using namespace std;
    #define For(i, x, y) for(int i=x;i<=y;i++)  
    #define _For(i, x, y) for(int i=x;i>=y;i--)
    #define Mem(f, x) memset(f,x,sizeof(f))  
    #define Sca(x) scanf("%d", &x)
    #define Scl(x) scanf("%lld",&x);  
    #define Pri(x) printf("%d
    ", x)
    #define Prl(x) printf("%lld
    ",x);  
    #define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
    #define LL long long
    #define ULL unsigned long long  
    #define mp make_pair
    #define PII pair<int,int>
    #define PIL pair<int,long long>
    #define PLL pair<long long,long long>
    #define pb push_back
    #define fi first
    #define se second 
    #define Vec Point
    typedef vector<int> VI;
    const double eps = 1e-9;
    const int maxn = 5e5 + 10;
    const int INF = 0x3f3f3f3f;
    const int mod = 1e9 + 7; 
    int N,M,tmp,K; 
    int value[30];
    char s1[maxn];
    char s2[maxn];
    int Next[maxn];
    LL sum[maxn];
    int cut[maxn][2];
    void KMP_Pre(char x[],int m){
        int i = 0,j = Next[0] = -1;
        while(i < m){
            while(j != -1 && x[i] != x[j]) j = Next[j];
            Next[++i] = ++j;
        }
    }
    int KMP(char x[],int m,char y[],int n){
        KMP_Pre(x,m);
        int i = 0,j = 0;
        while(i < m && j < n){
            while(j != -1 && y[i] != x[j]) j = Next[j];
            i++,j++; 
        }
        return j;
    }
    int main()
    {
        int T; Sca(T);
        while(T--){
            Mem(cut,0);
            For(i,0,25) Sca(value[i]);
            scanf("%s",s1); int l = strlen(s1);
            for(int i = 0 ; i < l ; i ++) s2[l - i - 1] = s1[i]; 
            for(int i = 1; i <= l ; i ++) sum[i] = sum[i - 1] + value[s1[i - 1] - 'a'];
            int k = KMP(s1,l,s2,l);
            while(k) cut[k][0] = 1,k = Next[k];
            k = KMP(s2,l,s1,l);
            while(k) cut[k][1] = 1,k = Next[k];
            LL ans = -INF;
            for(int i = 1; i < l; i ++){
                LL s = 0;
                if(cut[i][0]) s += sum[i];
                if(cut[l - i][1]) s += sum[l] - sum[i];
                ans = max(ans,s);
            }
            Prl(ans);
        }
        #ifdef VSCode
        system("pause");
        #endif
        return 0;
    }
    View Code
  • 相关阅读:
    国际关注,Panda 交易所获悉美银监机构批准特许银行托管加密资产
    Panda 交易所快报 央行数字货币测试进入C端流量入口
    Panda交易所获悉,五地股权市场获批参与「区块链建设试点」
    K2“拍了拍”你,这里有你想要的医药行业整体解决方案—K2 BPM
    K2 BPM 给你不一样的产品体验,有兴趣了解一下吗?
    BPM产品解读之规则设计器-K2 BPM-工作流引擎
    idea 使用Springboot 编译报错
    vue 表格中的下拉框单选、多选处理
    Kibana的安装和使用
    .net core 如何向elasticsearch中创建索引,插入数据。
  • 原文地址:https://www.cnblogs.com/Hugh-Locke/p/9520486.html
Copyright © 2011-2022 走看看