【GDOI2017】小学生语文题
【GDOI2017】小学生语文题
by AmanoKumiko
Description
已知串T,S,所有字母在两个串中出现次数相同,每次可以把S中后面的字符调至前面任意位置,求把S变成T的最少移动次数
Input
第一行一个整数N,表示N组数据
接下来每组数据两个串T和S
Output
每组数据第一行为最少移动次数
接下来为方案(spj)
Sample Input
3
abc
abc
sysu
ssyu
aaab
baaa
Sample Output
0
1
3 2
3
2 1
3 1
4 1
Data Constraint
\(len<=2000,N<=10\)
Solution
我一看这不是签到题吗?然后信仰贪心,发现是假的。。。
好吧省选Day2T3不是唬人///
正解:
类似于LCS的DP
设\(f[i][j]\)表示\(T\)串匹配了\(i-len\),\(S\)串取出了\(j-len\)的字符的最少移动次数
显然有三种转移
\(f[i+1][j]->f[i][j]\) 即已经取出的字符\(T[i]\)的个数足够匹配,前缀/后缀和判一下
\(f[i][j+1]+1->f[i][j]\) 即取出字符,但不一定立即匹配
\(f[i+1][j+1]->f[i][j]\) 即当前位相同,不需取字符
然而最恶心的是构造方案。。。
记录一个\(pre[i][j]\)表示属于哪种转移
如果是第二种则说明这个是被调动的字符,打个标记
然后枚举\(T\)的每个位置,若不同,则找出\(S\)中最近(自己想为什么不是最远的)的有标记的且字符相同的位置,暴力调动
Code
#include<bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(int i=a;i<=b;i++)
#define Fd(i,a,b) for(int i=a;i>=b;i--)
#define N 2010
char s[N],t[N];
int n,len,f[N][N],st[N][26],ss[N][26],num[N],nt,tag[N];
struct node{int a,b;}pre[N][N];
int main(){
freopen("chinese.in","r",stdin);
freopen("chinese.out","w",stdout);
scanf("%d",&n);
while(n--){
scanf("%s",t+1);
len=strlen(t+1);
scanf("%s",s+1);
F(i,1,len){
F(j,0,25)ss[i][j]=ss[i-1][j],st[i][j]=st[i-1][j];
st[i][t[i]-'a']++;ss[i][s[i]-'a']++;
}
memset(f,127,sizeof(f));
F(i,1,len+1)f[len+1][i]=len-i+1;
Fd(i,len,1) Fd(j,i,1){
if(st[len][t[i]-'a']-st[i-1][t[i]-'a']<=ss[len][t[i]-'a']-ss[j-1][t[i]-'a']){
if(f[i+1][j]<f[i][j])f[i][j]=f[i+1][j],pre[i][j]=(node){i+1,j};
}
if(t[i]==s[j]){
if(f[i+1][j+1]<f[i][j])f[i][j]=f[i+1][j+1],pre[i][j]=(node){i+1,j+1};
}
if(f[i][j+1]+1<f[i][j]){
f[i][j]=f[i][j+1]+1;pre[i][j]=(node){i,j+1};
}
}
printf("%d\n",f[1][1]);
node now=(node){1,1};nt=0;
memset(tag,0,sizeof(tag));
while(now.a<=len&&now.b<=len){
node last=pre[now.a][now.b];
if(last.a==now.a&&last.b==now.b+1)tag[now.b]=1;
else if(last.a==len+1){Fd(i,len,last.b)tag[i]=1;}
now=last;
}
F(i,1,len)if(t[i]!=s[i]){
int pos=0;
F(j,i+1,len)if(tag[j]&&s[j]==t[i]){pos=j;break;}
Fd(j,pos,i+1)swap(s[j],s[j-1]),swap(tag[j],tag[j-1]);
printf("%d %d\n",pos,i);
}
}
return 0;
}