Problem Description
Given a string with uppercase letters, and you are allowed to reverse the prefix by each operation. Your task is to make the string to become the smallest one in alphabetical order at most k operations. You should note that the length of the prefix you want to reverse must be larger than the previous operation except the first operation.
For example, given the string "BCDAF", and you are allowed to operate at most 2 times. If you reverse the prefix of length 3 ("BCD") to get "DCBAF", and then the length of the prefix you reverse next time should be larger than 3. After reversing the prefix of length 4 ("DCBA") to get "ABCDF", and it's the smallest one in alphabetical order.
The first line of the input contains an integer T (T≤50), indicating the number of cases. Each case begins with a line containing two integers N and k (0<N≤100, 0≤k≤N), the length of the string and the maximum times of operations you are allowed to do. The next line contains the string, consisting of characters 'A' to 'Z'.
For each test case, print a line containing the test case number (beginning with 1) and the smallest string you make in alphabetical order.
The 35th ACM/ICPC Asia Regional Fuzhou Site —— Online Contest
题意如下:
给出一个字符串,要求最多翻转它的前缀k次使得其字典序变成最小,但是每次翻转的前缀一定要比前一次大。
思路:
设两个数组:
1.f[s][i],表示第s轮前i个字符顺序最小的情况(如:BDCA < DBCA)。
2.g[s][i],表示第s轮前i个字符倒序最小的情况(如:DBCA < BDCA)。
每一轮翻转都记录前i个字符的最优翻转结果,状态转移可以由本轮前i-1个最小 f[s][i-1] 和上一轮的前i 个倒序最小 g[s^1][i-1] 再 翻转i个字符的较小者决定,当然也可能不做任何改动,就是f[s^1][i]。容易搞错的一个地方是:字符串的值最大翻转之后不一定最小!!比如DBCD的ascii值大于CEBA,翻转后依然是DCBD>ABEC。必须要倒序的最小才行。
#include<iostream>
using namespace std;
char f[2][102][102],g[2][102][102];
char str[102];
int i,j,t,n,k;
void work(int d)
{
int s=d%2,i,j,flag,k;
f[s][d-1][0]='z',g[s][d-1][d]='z';
for(i=d;i<n;i++)
{
//求f[s][i]
flag=0;
g[s^1][i-1][i]=str[i];
if(strcmp(f[s][i-1],f[s^1][i])>0)//本轮的最小和前一轮最小
{
flag=1;
for(j=0,k=i;j<=i;j++,k--){
if(g[s^1][i-1][k]>f[s^1][i][j]){
break;}
if(g[s^1][i-1][k]<f[s^1][i][j])
{ flag=2; break;}
}
}
else//再和翻转之后的比较
{
flag=0;
for(j=0,k=i;j<=i;j++,k--){
if(g[s^1][i-1][k]>f[s][i-1][j]){
break;}
if(g[s^1][i-1][k]<f[s][i-1][j])
{ flag=2; break;}
}
}
if(flag==0)//根据情况分别给F[S][I]赋值
strcpy(f[s][i],f[s][i-1]);
else
if(flag==1)
strcpy(f[s][i],f[s^1][i]);
else{
for(j=0,k=i;j<=i;j++,k--)
f[s][i][j]=g[s^1][i-1][k];
}
/*------------------------------------------分割线-------------------------------------------------*/
//求g[s][i]
flag=0;
for(j=i;j>=0;j--){
if(g[s][i-1][j]<g[s^1][i][j])
break;
if(g[s][i-1][j]>g[s^1][i][j])
{flag=1;break;}
}
if(flag)
{
for(j=0,k=i;j<=i;j++,k--){
if(f[s^1][i-1][j]>g[s^1][i][k]){
break;}
if(f[s^1][i-1][j]<g[s^1][i][k])
{ flag=2; break;}
}
}
else
{
for(j=0,k=i;j<=i;j++,k--){
if(f[s^1][i-1][j]>g[s][i-1][k]){
break;}
if(f[s^1][i-1][j]<g[s][i-1][k])
{ flag=2; break;}
}
}
if(flag==0)
strcpy(g[s][i],g[s][i-1]);
else
if(flag==1)
strcpy(g[s][i],g[s^1][i]);
else{
for(j=0,k=i;j<=i;j++,k--)
g[s][i][j]=f[s^1][i-1][k];
}
}
}
int main()
{
int c=0;
cin>>t;
while(t--)
{
scanf("%d%d",&n,&k);
scanf("%s",str);
if(k==n)
k=n-1;
if(n==1 || k==0)
{
printf("Case %d: %s/n",++c,str);
continue;
}
for(i=0;i<=n;i++)
{
strcpy(f[0][i],str);
strcpy(f[1][i],str);
strcpy(g[0][i],str);
strcpy(g[1][i],str);
}
for(i=1;i<=k;i++)
work(i);
f[k%2][n-1][n]='/0';
printf("Case %d: %s/n",++c,f[k%2][n-1]);
}
return 0;
}