Best Reward HDU - 3613(Manacher)

Best Reward HDU - 3613

题目链接:https://cn.vjudge.net/contest/163024#problem/S
题目大意:26个字母,每个都有一个权值,给定一字符串,将这个字符串分给成两部分,分割后的两部分,如果该部分是回文的话它的价值就是所有字母的权值和,否则就是0。

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

output

1
6
题目分析:可以预处理一下,这个串在第i点分隔开之后是否是回文,然后用前缀和处理一下权值即可。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e6 + 10;

int p[maxn], v[30], sum[maxn], cnt, len;
bool Left[maxn], Right[maxn];
char str[maxn], s[maxn];

void init()
{
    memset(p, 0, sizeof(p));
    int i;
    cnt = 0;
    str[cnt++] = '$';
    for(i = 0; i < len; i++)
    {
        str[cnt++] = '#';
        str[cnt++] = s[i];
    }
    str[cnt++] = '#';
    str[cnt] = '\0';
}

void manacher()
{
    init();
    int po = 0, maxr = 0;
    for(int i = 1; i < cnt; i++)
    {
        if(maxr > i) p[i] = min(p[po * 2 - i], maxr - i);
        else p[i] = 1;

        while(str[i + p[i]] == str[i - p[i]]) p[i]++;

        if(i + p[i] > maxr)
        {
            maxr = i + p[i];
            po = i;
        }
        if(p[i] == i) Left[i - 1] = true;
        if(p[i] + i  == cnt) Right[p[i] - 1] = true;
    }
}

int main()
{
    int t, i;
    scanf("%d", &t);
    while(t--)
    {
        for(i = 0; i < 26; i++)
            scanf("%d", &v[i]);

        scanf("%s", s);
        len = strlen(s);

        memset(sum, 0, sizeof(sum));
        memset(Left, false, sizeof(Left));
        memset(Right, false, sizeof(Right));
        sum[0] = v[s[0] - 'a'];
        for(i = 1; i < len; i++)
        {
            sum[i] = sum[i - 1] + v[s[i] - 'a'];
        }

        manacher();

        int ans = 0;
        for(i = 1; i < len; i++)
        {
            int tmp = 0;
            if(Left[i])
            {
                tmp += sum[i - 1];
            }
            if(Right[len - i])
            {
                tmp += sum[len - 1] - sum[i - 1];
            }

            ans = max(tmp, ans);

        }

        printf("%d\n", ans);
    }
}

弄不懂的地方是:

  if(p[i] == i) Left[i - 1] = true;
  if(p[i] + i  == cnt) Right[p[i] - 1] = true;

这两行代码。
举个例子:abaaba
处理之后的是这样的

位置:0 1 2 3 4 5 6 7 8 9 10 11 12 13
字符:$ # a # b # a # a #  b  #  a  #
p[i]:  1 2 1 4 1 2 7 2 1  4  1  2  1

p[4] = 4,这时left[3] = true; 它的意思是 前缀 aba是个回文串。因为p[4] = 4,说明在str[1~3]个字符与后边[5~7]个字符组成了回文(当然是加上了4个字符),因为这个字符串是加上了#的,所以p[4]=4,是说明一共有3 * 2 + 1个字符,对应回原来的就是i- 1;
right同理,只不过处理的是后缀。

阅读更多
上一篇最大最小表示法
下一篇Coconuts HDU - 5925 (bfs + 离散化)
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭