挑战
Description
定义一个游戏,一些规则如下:
1、一个字符串X被称为字符串Y的同构串当且仅当构成X和Y的字母完全相同(不计较次序),如“baba”与“abab”,“aabb”,“abba”互为同构串。
2、一个字符串X被称为字符串Y的 子序列当且仅当X可以通过将Y去掉若干个字母而不改变相对顺序获得,如“ac”,“abd”,“abcd”都是“abcd”的子序列。
3、一个字符串X被称为字符串Y的同构子序列当且仅当存在一个字符串Z,使得X是Z的同构串,Z是Y的子序列。
现在要求把一个给定字符串S从中间分开成m个子串S1,S2,…,Sm,使得S1+S2+…+Sm=S,且对于任意i<m,Si是Si+1的同构子序列。求最大化m。
1、一个字符串X被称为字符串Y的同构串当且仅当构成X和Y的字母完全相同(不计较次序),如“baba”与“abab”,“aabb”,“abba”互为同构串。
2、一个字符串X被称为字符串Y的 子序列当且仅当X可以通过将Y去掉若干个字母而不改变相对顺序获得,如“ac”,“abd”,“abcd”都是“abcd”的子序列。
3、一个字符串X被称为字符串Y的同构子序列当且仅当存在一个字符串Z,使得X是Z的同构串,Z是Y的子序列。
现在要求把一个给定字符串S从中间分开成m个子串S1,S2,…,Sm,使得S1+S2+…+Sm=S,且对于任意i<m,Si是Si+1的同构子序列。求最大化m。
Input
第一行包含一个整数n,表示字符串的长度。
第二行为1个长度为n的字符串,仅包含大写字母。
第二行为1个长度为n的字符串,仅包含大写字母。
Output
输出仅一行,一个整数,为最多可以分成的块数。
Sample Input
6
ABABAB
ABABAB
Sample Output
3
Hint
20%的数据:n≤10;
50%的数据:n≤100;
100%的数据:n≤500。
50%的数据:n≤100;
100%的数据:n≤500。
看到这题的时候我是懵逼的,数据很小题目也不复杂,可是本蒟蒻怎么看就是不会做,听了分析茅塞顿开。
有一个简单易想的算法,就是DP 设F[i][j]表示1~i中最后一块长度为j最多能分为多少块,并非满分算法。
这里,我们可以用贪心的思想让DP优化。
设F[i]表示1~i最后一块长度最小值
则最后一块的下标为 i-j+1~i
倒数第二块的下标为 i-j-f[i-j]+1~i-j
通过判断最后一块是否为倒数第二块的同构子序列来进行状态转移。这就是判断的函数
bool check(int x,int y,int z) { memset(b,0,sizeof(b)); for(int i=y;i<=z;i++) b[s[i]-'A']++; for(int i=x;i<y;i++) b[s[i]-'A']--; for(int i=0;i<26;i++) if(b[i]<0) return false; return true; }
由于我们希望序列越小越好,对后面越有利,因此枚举j的时候从小到大,发现可行的,就跳出循环。
int main() { scanf("%d\n",&n); scanf("%s",s+1); for(int i=0;i<=1000;i++) f[i]=i+1; for(int i=2;i<=n;i++) for(int j=1;j<=i;j++) if(check(i-j-f[i-j]+1,i-j+1,i)) { f[i]=j; break; } ans=0; for(int i=n;i>=1;i-=f[i]) ans++; printf("%d\n",ans); return 0; }
有几点要注意,首先每个Fi都要赋初值i+1
其次注意跳出循环,我调试了好几次才发现这个问题。
答案的计算很巧妙,从F[n]向前推。
下面贴上完整代码
#include "stdio.h" #include "string.h" #include "iostream" #include "algorithm" using namespace std; int n; char s[1005]; int f[1005]; int b[30]; int ans; bool check(int x,int y,int z) { memset(b,0,sizeof(b)); for(int i=y;i<=z;i++) b[s[i]-'A']++; for(int i=x;i<y;i++) b[s[i]-'A']--; for(int i=0;i<26;i++) if(b[i]<0) return false; return true; } int main() { scanf("%d\n",&n); scanf("%s",s+1); for(int i=0;i<=1000;i++) f[i]=i+1; for(int i=2;i<=n;i++) for(int j=1;j<=i;j++) if(check(i-j-f[i-j]+1,i-j+1,i)) { f[i]=j; break; } ans=0; for(int i=n;i>=1;i-=f[i]) ans++; printf("%d\n",ans); return 0; }
数组大小坑了我好几次,有点奇怪。
OVER~
祝我和学长99(✺ω✺)