codeforces 137D Palindromes(动规+路径记录)

题面:

Friday is Polycarpus' favourite day of the week. Not because it is followed by the weekend, but because the lessons on Friday are 2 IT lessons, 2 math lessons and 2 literature lessons. Of course, Polycarpus has prepared to all of them, unlike his buddy Innocentius. Innocentius spent all evening playing his favourite game Fur2 and didn't have enough time to do the literature task. As Innocentius didn't want to get an F, he decided to do the task and read the book called "Storm and Calm" during the IT and Math lessons (he never used to have problems with these subjects). When the IT teacher Mr. Watkins saw this, he decided to give Innocentius another task so that the boy concentrated more on the lesson and less — on the staff that has nothing to do with IT.

Mr. Watkins said that a palindrome is a string that can be read the same way in either direction, from the left to the right and from the right to the left. A concatenation of strings ab is a string ab that results from consecutive adding of string b to string a. Of course, Innocentius knew it all but the task was much harder than he could have imagined. Mr. Watkins asked change in the "Storm and Calm" the minimum number of characters so that the text of the book would also be a concatenation of no more than k palindromes. Innocentius can't complete the task and therefore asks you to help him.

Input

The first input line contains a non-empty string s which is the text of "Storm and Calm" (without spaces). The length of the string s does not exceed 500 characters. String s consists of uppercase and lowercase Latin letters. The second line contains a single number k (1 ≤ k ≤ |s|, where |s| represents the length of the string s).

Output

Print on the first line the minimum number of changes that Innocentius will have to make. Print on the second line the string consisting of no more than k palindromes. Each palindrome should be non-empty and consist of uppercase and lowercase Latin letters. Use the character "+" (ASCII-code 43) to separate consecutive palindromes. If there exist several solutions, print any of them.

The letters' case does matter, that is an uppercase letter is not considered equivalent to the corresponding lowercase letter.

Examples

Input

abacaba
1

Output

0
abacaba

Input

abdcaba
2

Output

1

abdcdba

Input

abdcaba
5

Output

0
a+b+d+c+aba

Input

abacababababbcbabcd
3

Output 

1
abacaba+babab+bcbabcb

题意:

就是给你一个字符串,然后给一个数字n,求这个字符串经过修改几次可以满足分为至多n段的回文串。并依据所给格式输出各段。

思路:

        这个题在做过一些模板题之后,至于修改次数的求解都比较简单,问题难题就是路径记录(虽然我也不会就是了),首先要考虑到动态规划(大部分回文串都可以),然后考虑改变的次数,此时状态应该满足子问题最优。

        首先,我们应知道每个字串成为回文字符串所需要的修改次数,于是定义一个ha[i][j]表示从第i位到第j位的子串成为回文串所需修改的次数。(包含i与j)

        考虑一下,如果len长度的字符串,到了第len位修改次数最少,此时第len-1位也应该修改次数最少,所以我们要考虑的状态便是到了第i个位置,不止如此,如果我们在第len个位置有最优,则前面部分必须有个位置pos有最优解,并在pos处进行了分割,在pos分割之前,有j段不连续的子串,在len时,有j+1的字串,所以我们只需枚举各字串的分割点位置,所以这个题便是个二维dp。

        dp[i][j]表示的是到了第j位前边分割成了i段的最优解。(注意,这个i段表示的只是不大于i,也可以是小于i的段数)。

        于是转移方程显而易见了

                dp[ i ][ j ] = min(dp[ i ][ j ], dp[ i-1 ] [ k ]+ ha[ k+1 ] [ j ])( i-1 <= k <= j )

        为什么要从 k = i - 1 开始呢,因为i - 1处是最少的能将长度为k的字串分为 i - 1的长度连续串,而当k = j 时便是前边不进行分割的状况。

        接下来是路径记录,既然我们已经找到了最优解,便只需倒推最优解,回到上一个得到该解 的位置,即从第n个位置推出来 n - 1,依次往前推动,得到第一个位置的解。

        但是,问题又出现了,路径记录时,像前边说的一样,会有不分割的状况,于是推出的两个连续的位置可能是一样的,所以我们还需要继续查重,查重之后,便可以得到所有分割点的位置,然后开始修改各字串,进行输出即可。

代码:

#include <bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1e4+5;
int ha[505][505];//第i位到第j位修改所需次数
int dp[505][505];//dp[i][j]表示到j位分成i段
int ans[505];//记录路径位置
char s[505];
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int n;
    cin>>(s+1)>>n;
    int len=strlen(s+1);
    memset(dp,inf,sizeof(dp));
    memset(ha,0,sizeof(ha));
    //先求第i位到j位修改次数
    for(int i=1;i<=len;i++){
        for(int j=1;j<=i;j++){
            ha[j][i]=ha[j+1][i-1]+(s[i]!=s[j]);
        }
    }
    dp[0][0]=0;
    for(int i=1;i<=n;i++){
        for(int j=i;j<=len;j++){
            //枚举各个分割点
            for(int k=i-1;k<=j;k++){
                if(dp[i-1][k]!=inf)
                    dp[i][j]=min(dp[i][j],dp[i-1][k]+ha[k+1][j]);
            }
        }
    }
    cout<<dp[n][len]<<endl;
    int cnt=0,it=len;
    for(int i=n;i>=1;i--){
        //找到第i个位置的上一个位置
        for(int j=1;j<=len;j++){
            if(dp[i-1][j]!=inf&&dp[i][it]==dp[i-1][j]+ha[j+1][it]){
                it=j;
                ans[i]=j+1;
                break;
            }
        }
    }
    //初始情况最后一段便是不分割len+1,第一段即第一个位置
    ans[n+1]=len+1;
    ans[1]=1;
    cnt=1;
    //进行查重
    for(int i=1;i<=n+1;i++){
        if(ans[i-1]!=ans[i])
            ans[cnt++]=ans[i];
    }
    cnt-=2;
    ans[cnt+1]=len+1;
    //对各个子段进行修改
    for(int i=1;i<=cnt;i++){
        int l=ans[i],r=ans[i+1]-1;
        while(l<r){
            s[r]=s[l];
            l++;
            r--;
        }
    }
    cnt=2;
    //输出
    for(int i=1;i<=len;i++){
        cout<<s[i];
        if(i==ans[cnt]-1){
            if(ans[cnt]!=len+1)cout<<"+";
            cnt++;
        }
    }
    cout<<endl;
    return 0;
}
/*
abdcaba
2
abdcaba
5
abacababababbcbabcd
3
*/

参考链接:Codeforces 137D - Palindromes(区间DP) - 代码天地 (codetd.com)(侵删)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值