ACM新手DAY 12 扩展KMP+AC自动机+Manacher

题解

A - Best Reward(样题)

题目: 将一个串分割成两个串,你得到得代价就是两个子串的代价和,子串的代价是这样得到的:
1、如果子串是回文串,那么代价就是子串每一个位置值的和。
2、反之,代价是0。

分别给出KMP扩展和Manacher算法两种代码,详细解释在注释里。

  • Manacher算法
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 500010;
struct Manacher
{
	char Ma[MAXN << 1];
	int Mp[MAXN << 1];
	int work(char s[])//黑匣子之mp数组的获得
	{
		int len = strlen(s), l = 0;
		Ma[l++] = '$', Ma[l++] = '#';
		int k = 0, p = 0;
		for (int i = 0; i < len; i++) Ma[l++] = s[i], Ma[l++] = '#';
		Ma[l] = '\0';
		for (int i = 0; i < l; i++)
		{
			Mp[i] = p > i ? min(Mp[2 * k - i], p - i) : 1;
			while (Ma[i + Mp[i]] == Ma[i - Mp[i]]) Mp[i]++;
			if (i + Mp[i] > p)
			{
				p = i + Mp[i];
				k = i;
			}
		}
		return l;
	}
} M;
int a[26];
char s[500010];
int ps[500010];
int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		for (int i = 0; i < 26; i++) scanf("%d", &a[i]);//读入26个字母的权重
		scanf("%s", s);
		int len = strlen(s);//字符串读入以及长度获取
		for (int i = 1; i <= len; i++) ps[i] = ps[i - 1] + a[s[i - 1] - 'a'];//ps数组记录字符串中字符对应的权重
		len = M.work(s);//将字符串做扩展处理,并且len的含义已经变了,变成了扩展后的长度
		int ans = 0;
		for (int i = 2; i < len / 2; i++)
		{
		    //res每次都会重置为0,注意,这里两个if是对分成的两端分别分析是否可以构成回文
			int res = 0;
			if (M.Mp[i] == i) res += ps[i - 1];
			if (M.Mp[len / 2 + i - 1] + len / 2 + i - 1 == len)
				res += ps[len / 2 - 1] - ps[i - 1];
			ans = max(res, ans);//每次取收益最大
		}
		printf("%d\n", ans);
	}
	return 0;
}
  • KMP扩展
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 500010;
int a[26];
char S[MAXN], T[MAXN];
int sum[MAXN];
struct e_KMP
{
    int Next[MAXN], extend[MAXN];
    char *S, *T;
    int lenS, lenT;

    void init(char* S, char* T, int len)
    {
        this->S = S, this->T = T, this->lenS = this->lenT = len;
    }
    void get_next()
    {
        Next[0] = lenT, Next[1] = 0;
        while (Next[1] + 1 < lenT && T[Next[1]] == T[Next[1] + 1])
            Next[1]++;
        int k = 1, p = Next[1];
        for (int i = 2; i < lenT; i++)
        {
            if (p > i + Next[i - k] - 1) Next[i] = Next[i - k];
            else
            {
                Next[i] = max(0, p - i + 1);
                while (i + Next[i] < lenT && T[i + Next[i]] == T[Next[i]]) Next[i]++;
                k = i, p = i + Next[i] - 1;
            }
        }
    }
    void work()
    {
        get_next(), extend[0] = 0;
        while (extend[0] < lenS && extend[0] < lenT && S[extend[0]] == T[extend[0]])
            extend[0]++;
        int k = 0, p = extend[0] - 1;
        for (int i = 1; i < lenS; i++)
        {
            if (p > i + Next[i - k] - 1) extend[i] = Next[i - k];
            else
            {
                extend[i] = max(0, p - i + 1);
                while (i + extend[i] < lenS && extend[i] < lenT
                        && S[i + extend[i]] == T[extend[i]])
                    extend[i]++;
                k = i, p = i + extend[i] - 1;
            }
        }
    }
    /*void debug()
    {
        for (int i = 0; i < lenT; i++)cout << Next[i] << " ";
        cout << endl;
        for (int i = 0; i < lenS; i++)cout << extend[i] << " ";
        cout << endl;
    }*/
} EKMP, IEKMP;
int main()
{
    int t;
    scanf("%d", &t);
    while (t--)
    {
        for (int i = 0; i < 26; i++)scanf("%d", &a[i]);//读入26个字母的权值
        scanf("%s", S);
        int len = strlen(S);
        for (int i = 0; i < len; i++) T[i] = S[len - i - 1];//字符串反转部分
        T[len] = '\0';
        for (int i = 1; i <= len; i++) sum[i] = sum[i - 1] + a[S[i - 1] - 'a'];//赋权值吧
        EKMP.init(S, T, len), IEKMP.init(T, S, len);
        EKMP.work(), IEKMP.work();
        //IEKMP.debug();
        int ans = 0;
        for (int i = 1; i < len; i++)
        {
            //把原来的串一分为二啊,这里与manacher算法很像
            int res = 0;
            if (EKMP.extend[i] == len- i) res += sum[len] - sum[i];
            if (IEKMP.extend[len - i] == i)res += sum[i];
            ans = max(ans, res);//找收益最大呗
        }
        printf("%d\n", ans);
    }
    return 0;
}

H - 最长回文

Manacher算法对Mp数组的应用.
其他的基本没有变化,这里是main函数部分。

int main ()
{
    while (scanf ("%s",s) != EOF)//读入字符串
    {
        int len = strlen(s),ans = 0;
        Manacher(s,len);
        for (int i = 0; i < 2*len+2; i++)
        {
            ans = max(ans,Mp[i] - 1);//只要最长的回文串
        }
        cout << ans <<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值