Wannafly挑战赛19 D.回文 回文树或Manacher 前缀和

链接:https://www.nowcoder.com/acm/contest/131/D
来源:牛客网
 

题目描述

字符串 S 只包含小写英文字母。有四种操作,每次操作你可以选择其中一种:

删除字符串的第一个字母。
删除字符串的最后一个字母。
在字符串的头部添加任意一个你想要的字母。
在字符串的尾部添加任意一个你想要的字母。

删除一个第 i 种英文字母需要的花费是 Ai,添加一个第 i 种英文字母的花费是 Bi。
请问将字符串 S 变成回文串需要的最小花费是多少?

输入描述:

第一行输入一个字符串 S。
接下来 26 行,每行输入两个正整数 Ai 和 Bi,表示删除一个第 i 种字符所需的花费以及添加一个第 i 种字符所需的花费。
1≤ |S| ≤ 105 且字符串 S 中只包含小写英文字母.
1≤ Ai,Bi≤ 109.

输出描述:

输出一个正整数,表示把字符串 S 变成一个回文串的最小花费。

题解:

答案一定是保留某个回文,然后一边保留一部分,另一边变成这部分。回文树找i位置最长回文,前缀预处理一下即可。

代码:

#include <bits/stdc++.h>
#ifdef LOCAL
#define debug(x) cout<<#x<<" = "<<(x)<<endl;
#else
#define debug(x) 1;
#endif

#define chmax(x,y) x=max(x,y)
#define chmin(x,y) x=min(x,y)
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
#define lowbit(x) x&-x
#define mp make_pair
#define pb push_back
#define fir first
#define sec second
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

const int MOD = 1e9 + 7;
const double PI = acos (-1.);
const double eps = 1e-10;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3f;
const int MAXN = 5e5 + 5;

const int N = 26 ;
struct Palindromic_Tree {
    int nxt[MAXN][N] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
    int fail[MAXN] ;//fail指针,失配后跳转到fail指针指向的节点
    int cnt[MAXN] ; //i表示的回文串在原串出现多少次(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的)
    int num[MAXN] ; //表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数
    int len[MAXN] ;//len[i]表示节点i表示的回文串的长度(一个节点表示一个回文串)
    int S[MAXN] ;//存放添加的字符
    int last ;//指向新添加一个字母后所形成的最长回文串表示的节点。
    int n ;//表示添加的字符个数。
    int p ;//表示添加的节点个数。

    int newnode ( int l ) {//新建节点
        for ( int i = 0 ; i < N ; ++ i ) nxt[p][i] = 0 ;
        cnt[p] = 0 ;
        num[p] = 0 ;
        len[p] = l ;
        return p ++ ;
    }

    void init () {//初始化
        p = 0 ;
        newnode (  0 ) ;
        newnode ( -1 ) ;
        last = 0 ;
        n = 0 ;
        S[n] = -1 ;//开头放一个字符集中没有的字符,减少特判
        fail[0] = 1 ;
    }

    int get_fail ( int x ) {//和KMP一样,失配后找一个尽量最长的
        while ( S[n - len[x] - 1] != S[n] ) x = fail[x] ;
        return x ;
    }

    int add ( int c ) {
        S[++ n] = c ;
        int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置
        if ( !nxt[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
            int now = newnode ( len[cur] + 2 ) ;//新建节点
            fail[now] = nxt[get_fail ( fail[cur] )][c] ;//和AC自动机一样建立fail指针,以便失配后跳转
            nxt[cur][c] = now ;
            num[now] = num[fail[now]] + 1 ;
        }
        last = nxt[cur][c] ;
        cnt[last] ++ ;
        return len[last];
    }

    void count () {
        for ( int i = p - 1 ; i >= 0 ; -- i ) cnt[fail[i]] += cnt[i] ;
        //父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
    }
} pam;

char s[MAXN];
int cost1[MAXN], cost2[MAXN];
ll pre1[MAXN], pre2[MAXN];
ll minn[MAXN];

ll solve (int n) {
    minn[n + 1] = INFLL;
    for (int i = 1; i <= n; i++) {
        pre1[i] = pre1[i - 1] + cost1[s[i] - 'a'];
        pre2[i] = pre2[i - 1] + cost2[s[i] - 'a'];
    }
    for (int i = n; i >= 1; i--) minn[i] = min(minn[i + 1], pre2[i] - pre1[i]);
    pam.init();
    ll ret = INFLL;
    for (int i = 1; i <= n; i++) {
        int len = pam.add(s[i] - 'a');
        ret = min(ret, pre1[i - len] + minn[i] + pre1[n] - pre2[i]);
    }
    return ret;
}


int main() {
#ifdef LOCAL
    freopen ("input.txt", "r", stdin);
#endif
    scanf ("%s", s + 1);
    int n = strlen(s + 1);
    for (int i = 0; i < 26; i++) scanf ("%d %d", &cost1[i], &cost2[i]);
    ll ans = INFLL;
    chmin(ans, solve(n));
    reverse(s + 1, s + 1 + n);
    chmin(ans, solve(n));
    printf ("%lld\n", ans);
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值