zoukankan      html  css  js  c++  java
  • HDU3613 Best Reward —— Manacher算法 / 扩展KMP + 枚举

    题目链接:https://vjudge.net/problem/HDU-3613

    Best Reward

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
    Total Submission(s): 3104    Accepted Submission(s): 1277


    Problem Description
    After an uphill battle, General Li won a great victory. Now the head of state decide to reward him with honor and treasures for his great exploit. 

    One of these treasures is a necklace made up of 26 different kinds of gemstones, and the length of the necklace is n. (That is to say: n gemstones are stringed together to constitute this necklace, and each of these gemstones belongs to only one of the 26 kinds.) 

    In accordance with the classical view, a necklace is valuable if and only if it is a palindrome - the necklace looks the same in either direction. However, the necklace we mentioned above may not a palindrome at the beginning. So the head of state decide to cut the necklace into two part, and then give both of them to General Li. 

    All gemstones of the same kind has the same value (may be positive or negative because of their quality - some kinds are beautiful while some others may looks just like normal stones). A necklace that is palindrom has value equal to the sum of its gemstones' value. while a necklace that is not palindrom has value zero. 

    Now the problem is: how to cut the given necklace so that the sum of the two necklaces's value is greatest. Output this value. 

     
    Input
    The first line of input is a single integer T (1 ≤ T ≤ 10) - the number of test cases. The description of these test cases follows. 

    For each test case, the first line is 26 integers: v1, v2, ..., v26 (-100 ≤ vi ≤ 100, 1 ≤ i ≤ 26), represent the value of gemstones of each kind. 

    The second line of each test case is a string made up of charactor 'a' to 'z'. representing the necklace. Different charactor representing different kinds of gemstones, and the value of 'a' is v1, the value of 'b' is v2, ..., and so on. The length of the string is no more than 500000. 

     
    Output
    Output a single Integer: the maximum value General Li can get from the necklace.
     
    Sample Input
    2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 aba 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 acacac
     
    Sample Output
    1 6
     
    Source
     
    Recommend
    lcy

    题解:

    1.求出字符串值的前缀和以及后缀和。

    2.用Manacher算法或者扩展KMP算法求出:每个前缀和每个后缀是否为回文串。

    3.枚举分分割位置,然后再根据左右段字符串是否为回文串来进行求值。

    4.Manacher算法的认识在处理的时候,如果以“#”为中点,那么实际的回文串长度为偶数;如果以输入的字符为中点,那么实际的回文串长度为奇数。

    Manacher:

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <cstdlib>
     5 #include <string>
     6 #include <vector>
     7 #include <map>
     8 #include <set>
     9 #include <queue>
    10 #include <sstream>
    11 #include <algorithm>
    12 using namespace std;
    13 typedef long long LL;
    14 const double eps = 1e-6;
    15 const int INF = 2e9;
    16 const LL LNF = 9e18;
    17 const int MAXN = 5e5+10;
    18 
    19 char s[MAXN];
    20 int val[30], pre[MAXN], suf[MAXN];
    21 int sum[MAXN][2];
    22 
    23 int Ma[MAXN<<1], Mp[MAXN<<1];
    24 void Manacher(char *s, int len)
    25 {
    26     int l = 0;
    27     Ma[l++] = '$'; Ma[l++] = '#';
    28     for(int i = 0; i<len; i++)
    29     {
    30         Ma[l++] = s[i];
    31         Ma[l++] = '#';
    32     }
    33     Ma[l] = 0;
    34 
    35     int mx = 0, id = 0;
    36     for(int i = 1; i<l; i++)
    37     {
    38         Mp[i] = mx>=i?min(Mp[2*id-i], mx-i):0;
    39         while(Ma[i-Mp[i]-1]==Ma[i+Mp[i]+1]) Mp[i]++;
    40         if(i+Mp[i]>mx)
    41         {
    42             mx = i+Mp[i];
    43             id = i;
    44         }
    45 
    46         //记录以s[i/2-1]为中点(长度为奇时,为中点;长度为偶时,为左半段的末点)时的最长回文串
    47         //如果处理的时候,以“#”为中点,那么实际的长度为偶数
    48         //如果处理的时候,以输入的字符为中点,那么实际的长度为奇数
    49         if(i>=2) sum[i/2-1][Ma[i]!='#'] = Mp[i];
    50     }
    51 }
    52 
    53 int main()
    54 {
    55     int T;
    56     scanf("%d", &T);
    57     while(T--)
    58     {
    59         for(int i = 0; i<26; i++)
    60             scanf("%d", &val[i]);
    61 
    62         scanf("%s", s);
    63         int len = strlen(s);
    64         pre[0] = val[s[0]-'a'];
    65         suf[len-1] = val[s[len-1]-'a'];
    66         for(int i = 1; i<len; i++)
    67         {
    68             pre[i] = pre[i-1] + val[s[i]-'a'];  //求前缀和
    69             suf[len-i-1] = suf[len-i] + val[s[len-i-1]-'a'];    //求后缀和
    70         }
    71 
    72         memset(sum, 0, sizeof(sum));
    73         Manacher(s, len);
    74 
    75         int ans = -INF;
    76         for(int i = 0; i<=len-2; i++)
    77         {
    78             int Lmid = i/2, Rmid = (i+1+len-1)/2;   //找到左右两段的中点
    79             int Llen = i+1, Rlen = len-Llen;        //求出左右两段的长度
    80 
    81             int tmp = 0;
    82             if(sum[Lmid][Llen%2]==Llen) tmp += pre[i];  //如果左段为回文串
    83             if(sum[Rmid][Rlen%2]==Rlen) tmp += suf[i+1];    //如果右段为回文串
    84             ans = max(ans, tmp);
    85         }
    86         printf("%d
    ", ans);
    87     }
    88 }
    View Code

    扩展KMP:

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <cstring>
      4 #include <cstdlib>
      5 #include <string>
      6 #include <vector>
      7 #include <map>
      8 #include <set>
      9 #include <queue>
     10 #include <sstream>
     11 #include <algorithm>
     12 using namespace std;
     13 typedef long long LL;
     14 const double EPS = 1e-6;
     15 const int INF = 2e9;
     16 const LL LNF = 9e18;
     17 const int MOD = 1e9+7;
     18 const int MAXN = 5e5+10;
     19 
     20 void pre_EXKMP(char x[], int m, int Next[])
     21 {
     22     Next[0] = m;
     23     int j = 0;
     24     while(j+1<m && x[0+j]==x[1+j]) j++;
     25     Next[1] = j;
     26     int k = 1;
     27     for(int i = 2; i<m; i++)
     28     {
     29         int p = Next[k]+k-1;
     30         int L = Next[i-k];
     31         if(i+L<=p) Next[i] = L;
     32 
     33         else
     34         {
     35             j = max(0, p-i+1);
     36             while(i+j<m && x[i+j]==x[0+j]) j++;
     37             Next[i] = j;
     38             k = i;
     39         }
     40     }
     41 }
     42 
     43 //next[i]: x[i...m-1] 与 x[0...m-1]的最长公共前缀
     44 //exd[i]: y[i...n-1] 与 x[0...m-1]的最长公共前缀
     45 void EXKMP(char x[], int m, char y[], int n, int Next[], int exd[])
     46 {
     47     pre_EXKMP(x,m,Next);    //获取x的next数组(x与自己的匹配)
     48     int j = 0;
     49     while(j<n && j<m && x[j]==y[j]) j++;
     50     exd[0] = j;
     51     int k = 0;  //k使得k+exd[k]-1最大,即匹配得最远的y后缀
     52     for(int i = 1; i<n; i++)
     53     {
     54         int p = exd[k]+k-1; //p为y字符串匹配得最远的位置
     55         int L = Next[i-k];  //因为y[k...k+exd[k]-1] == x[0...exd[k]-1]
     56                             //所以y[i...k+exd[k]-1] == x[i-k...exd[k]-1]
     57                     //我们已知:  x[i-k...] 与 x[0...]的最长公共前缀为next[i-k]
     58         //那么我们可以利用next[i-k]的到:y[i...] 与 x[0...]至少匹配了多少个字符
     59         //然后i+next[i-k]就是以i为起始,目前为止匹配得最远的地方。
     60         //如果i+next[i-k]<=p,即小于等于之前匹配最远的,那么exd[i]就等于i+next[i-k]
     61         //否则,之后的字符不能确定是否匹配,所以要继续匹配下去,然后设置k为i
     62         if(i+L<=p) exd[i] = L;
     63         else
     64         {
     65             j = max(0,p-i+1);   //p-i+1即通过next[i-k]得知的y[i...]与x[0...]至少匹配的字符个数
     66             while(i+j<n && j<m && y[i+j]==x[0+j]) j++;
     67             exd[i] = j;
     68             k = i;
     69         }
     70     }
     71 }
     72 
     73 char s1[MAXN], s2[MAXN];
     74 int val[30], Next[MAXN], exd1[MAXN], exd2[MAXN], sum[MAXN];
     75 
     76 int main()
     77 {
     78     int T;
     79     scanf("%d",&T);
     80     while(T--)
     81     {
     82         for(int i = 0; i<26; i++)
     83             scanf("%d",&val[i]);
     84         scanf("%s",s1);
     85 
     86         int len = strlen(s1);
     87         sum[0] = val[s1[0]-'a'];
     88         for(int i = 1; i<len; i++)
     89             sum[i] = sum[i-1] + val[s1[i]-'a'];
     90 
     91         memcpy(s2, s1, sizeof(s2)); //复制并反转
     92         reverse(s2, s2+len);
     93 
     94         EXKMP(s1, len, s2, len, Next, exd1);    //前缀
     95         EXKMP(s2, len, s1, len, Next, exd2);    //后缀
     96         int ans = 0;
     97         reverse(exd1, exd1+len);    //这样exd1就是前缀, exd2就是后缀了。
     98         for(int i = 0; i<len-1; i++)
     99         {
    100             int tmp = 0;
    101             if(exd1[i]==i+1) tmp += sum[i];
    102             if(exd2[i+1]==len-(i+1)) tmp += sum[len-1]-sum[i];
    103             ans = max(ans,tmp);
    104         }
    105         printf("%d
    ", ans);
    106     }
    107 }
    View Code
  • 相关阅读:
    12-14面向对象--抽象基类、接口、委托
    关于 try catch catch
    C# using 三种使用方式
    互斥锁(Mutex)
    C#中Monitor类、Lock关键字和Mutex类
    System.Data.SQLite
    Dictionary<TKey, TValue> 类
    AttributeTargets 枚举
    C# is和as操作符
    合并委托(多路广播委托)
  • 原文地址:https://www.cnblogs.com/DOLFAMINGO/p/7895005.html
Copyright © 2011-2022 走看看