回文字符序列——微软编程之美2015资格赛第2题题解

来源:互联网 发布:北京集佳待遇知乎 编辑:程序博客网 时间:2024/05/29 16:55

描述

给定字符串,求它的回文子序列个数。回文子序列反转字符顺序后仍然与原序列相同。例如字符串aba中,回文子序列为"a", "a", "aa", "b", "aba",共5个。内容相同位置不同的子序列算不同的子序列。

输入

第一行一个整数T,表示数据组数。之后是T组数据,每组数据为一行字符串。

输出

对于每组数据输出一行,格式为"Case #X: Y",X代表数据编号(从1开始),Y为答案。答案对100007取模。

数据范围

1 ≤ T ≤ 30

小数据

字符串长度 ≤ 25

大数据

字符串长度 ≤ 1000



样例输入:
5abaabcbaddabcba12111112351121cccccccfdadfa
样例输出:
Case #1: 5Case #2: 277Case #3: 1333Case #4: 127Case #5: 17

动态规划的题,一开始想了很久,没想出来。后来经朋友指点,发现是自己思路不对。不应该从头尾一起算,直接从左到右,老老实实建表,更新结果就可以做出来了。

从字符串左边开始,每添加一个字符就更新一次表,表中保存的是该字符后的子串包含的回文序列数。
以样例的fdadfa为例:
初始化: res=0 表为空
输入f: 将f加入表,map[0].char = 'f', f后面的子串为空,map[0].num=0

char后面子串numres0f“”01
string = "f", res = 1

输入d: 将d加入表,map[1].char = 'd',map[1].num=0, 
f后面的子串为d,map[0].num=1

char后面子串numres0f"d"1
1d""02
string = "fd", res = 2

输入a: 将a加入表,

char后面子串numres0f"da"2
1d"a"1
2a""03
string = "fda", res = 3

输入d:将a加入表,map[3].char = 'd',map[3].num=0, 
a后面的子串为d,map[2].num=1;
d后面的子串为ad,map[1].num=2
f后面的子串为dad,map[0].num=5,

char后面子串numres0f"dad"5
1d"ad"2
2a"d"1
3d""06
注意到这个时候因为新加入的第四个字符d与前面的第二个字符d相同,所以f后面的子串dad包含了5个回文子序列,如果第四个字符不是d,而是其他不重复的字符,此时map[0].num应该等于3,所以多出来的两个回文子序列是dd以及夹在dd中间的回文子序列个数,即更新前的map[1].num

string ="fdad",同理res本来应该等于4,因为遇到相同字符,此时多出来的也是dd和夹在dd中间的回文子序列个数,
故res=3(上一次结果)+1(末尾新加入字符)+1(dd)+1(夹在dd中间的回文子序列个数map[1].num)=6
而且我们看到,计算res是用表格更新前的map[1].num,所以应该先计算res,再更新表

输入f: 
string ="fdadf",res = 6(上一次)+1(末尾新加入字符)+1(ff)+5(夹在ff之间的回文子序列个数map[0].num)=13
然后更新表:

char后面子串numres0f"dadf"6
1d"adf"3
2a"df"2
3d"f"1
4f""013

最后输入a: 
string ="fdadfa",res = 13(上一次)+1(末尾新加入字符)+1(aa)+2(夹在aa之间的回文子序列个数map[2].num)=17
然后更新表:

char后面子串numres0f"dadfa"10
1d"adfa"7
2a"dfa"3
3d"fa"2
4f"a"1
5a""0
将a加入表,map[5].char ='a',map[5].num=0;
我们用一个累计变量new(代码中的get_sum)表示因为末尾添加了新字符而新增的回文子序列个数,此时new=1,更新公式map[i].num+=new 
f后面子串为a,更新前map[4].num =0; 更新后map[4].num = 1;

d后面子串为fa,更新前map[3].num = 1,此时new=1,更新后map[3].num=2;
a后面子串为dfa,更新前map[2].num=2,此时new=1,更新后map[2].num = 3; 
因为当前字符a与新加入字符a相同,所以从下面的开始,new = 1(新加入字符a)+1(aa)+2(夹在aa之间的回文子序列个数map[2].num)=4
d后面子串为adfa,更新前map[1].num=3,此时new=4,更新后map[1].num = 3 + new =7;
f后面子串为dadfa,更新前map[0].num=6,此时new=4,更新后map[1].num = 6 + new =10;
由以上分析可以得到res和new两个变量的更新公式,用代码实现如下:

#include <vector>#include <iostream>#include <string>using namespace std; struct Node {      int num;      char my_char;  };int huiWenNum(string s){vector<Node> vec;int i,j,res=0, get_sum=0;for(i=0;i<s.size();i++){res++;if(!vec.empty()){for(j=vec.size()-1;j>=0;j--){if(vec[j].my_char==s[i]){res = res+1+vec[j].num;}}}Node tmp;tmp.my_char=s[i];tmp.num=0;vec.push_back(tmp);if(vec.size()>1){get_sum=1;for(j=vec.size()-2;j>=0;j--){if(vec[j].my_char!=s[i]){vec[j].num+=get_sum;}else{//meet the same charint tmp_num = vec[j].num;//storage the num for plusvec[j].num+=get_sum;get_sum = get_sum+1+tmp_num;//}}/*cout<<"the map is:"<<endl;for(j=0;j<vec.size();j++){cout<<vec[j].my_char<<' '<<vec[j].num<<endl;}*/}}return res%100007;}int main(){int i,nSample,res;string s;cin>>nSample;int head,tail,j,k;for (i=0;i<nSample;i++){cin>>s;res = huiWenNum(s);cout<<"Case #"<<i+1<<": "<<res<<endl;}//system("pause");return 0;}


这道题我只在小数据AC,大数据没有过。最后取模100007的步骤做得太轻率了,求res和更新表格的时候,每超过100007就取一次模可能好一些。毕竟是结果是指数级别,int存不下那么大的数字。
0 0
原创粉丝点击