添加最少字符到数组中组合成回文

来源:互联网 发布:访客网络账号密码 编辑:程序博客网 时间:2024/05/01 14:06
【问题描述】
回文词是一种对称的字符串——也就是说,一个回文词,从左到右读和从右到左读得到
的结果是一样的。任意给定一个字符串,通过插入若干字符,都可以变成一个回文词。你的
任务是写一个程序,求出将给定字符串变成回文词所需插入的最少字符数。
比如字符串“Ab3bd ”,在插入两个字符后可以变成一个回文词(“ dAb3bAd ”或
“Adb3bdA”)。然而,插入两个以下的字符无法使它变成一个回文词。
【输入文件】
第一行包含一个整数 N,表示给定字符串的长度,3<=N<=5000
第二行是一个长度为 N 的字符串,字符串由大小写字母和数字构成。
【输出文件】
一个整数,表示需要插入的最少字符数。
【输入样例】
5
Ab3bd
【输出样例】

2

 分析:由于回文是向右和向左读的都是一样的,那么我们可以这样思考,建立另一个数组,该数组是原数组的反方向的,那么假设两者读的都是一样的,那么这两者就是相同的,也就是说他们两者组合成了回文,因此题目就化为了求最长公共子序列的问题了

code:

#include<stdio.h>#include<climits>#include<algorithm>#include<stack>#include<iostream>#include<cmath>#include<set>#include<vector>#include<map>#include<queue>#include<string.h>using namespace std;const int maxn=10000;int c[2][maxn];char str1[maxn];char str2[maxn];int  main(void){  int n;  while(scanf("%d",&n)!=EOF)  {       memset(c,0,sizeof(c));       getchar();       for(int i=1;i<=n;i++)          {    scanf("%c",&str1[i]);    str2[n-i+1]=str1[i];   } int flag=1; for(int i=1;i<=n;i++) {    for(int j=1;j<=n;j++)    {       if(str1[i]==str2[j])       {     c[flag][j]=c[1-flag][j-1]+1;          }          else           {            c[flag][j]=max(c[flag][j-1],c[1-flag][j]);              }              cout<<c[flag][j]<<" ";    }    cout<<endl;    flag=1-flag; } cout<<c[1-flag][n]<<endl;     }  return 0;}

滚动数组优化空间

另外我查阅资料更新本文章的另一种方案,可以模拟一下,本算法不是很难的:

通过前面的分析我们很容易想到这样的算法:
对于一个序列 S 只要看它的左右端的字符是否相同,如果相同那么就看除掉两端字符的新串
要添的字符个数了;如果不同,就在它左面添上右断的字符然后考虑去掉新序列两端的字符
后的串要添的字符。或者在右面添上左端的字符,在考虑去掉添了字符后新串左右两端字符
得到的新串要添的字符。
设计一个二维状态 opt[L,i]表示长度是 L+1,起点是 i 的序列变成回文词要添的字符的个数。
阶段就是字符的长度,决策要分类,即 S[i]  和 S[i+L]是否相等。
状态转移方程:
min(opt[L-1,i]+1, opt[L-1,i+1]+1)           (s[i]<>s[i+L])
opt[L,i]=
min(opt[L-1,i]+1, opt[L-1,i+1]+1,opt[L-2,i+1])   (s[i]=s[i+L])
复杂度:
空间复杂度=状态数 O(N2)
时间复杂度=状态数 O(N2)*  转移代价 O(1)=O(N2)
由于空间复杂度较高,仍然要用滚动数组。

0 0
原创粉丝点击