正题
题面链接:https://www.luogu.com.cn/problem/U142584
题目大意
nnn个010101串,按顺序分成两个序列,然后拼接成一个序列(拼接串x,yx,yx,y的话就是变成一个前缀包含xxx,后缀包含yyy的最短的串)。求最短长度。
解题思路
显然将010101串的状态压起来
定义prex,ipre_{x,i}prex,i表示串xxx的前iii位,sufx,isuf_{x,i}sufx,i表示串xxx的后iii位,comx,ycom_{x,y}comx,y表示串x,yx,yx,y的最长公共位。
那么设fi,j,kf_{i,j,k}fi,j,k表示到第iii个串,第一个串以aia_iai结尾,第二个的后jjj位是kkk时的最小长度和。
那么第一种转移就是拼接ai−1a_{i-1}ai−1和aia_iai,也就是fi,j,k=fi−1,j,k+L−comai−1,aif_{i,j,k}=f_{i-1,j,k}+L-com_{a_{i-1},a_i}fi,j,k=fi−1,j,k+L−comai−1,ai
第二种是aia_iai和kkk拼起来,那么第一个串的kkk就变成了ai−1a_{i-1}ai−1
fi,j,suf(ai−1,j)=fi−1,j,pre(ai,j)+L−jf_{i,j,suf(a_{i-1},j)}=f_{i-1,j,pre(a_i,j)}+L-jfi,j,suf(ai−1,j)=fi−1,j,pre(ai,j)+L−j
这样转移是O(n∗l∗2l)O(n*l*2^l)O(n∗l∗2l)的,显然无法通过本题
发现主要的时间落在第一个转移上,我们考虑优化掉第一个转移,我们发现每次的L−comai−1,aiL-com_{a_{i-1},a_i}L−comai−1,ai是一个定值,我们可以先让最后的答案加上这些定值,然后第一个转移可以去掉,二个转移变为fi,j,suf(ai−1,j)=fi−1,j,pre(ai,j)+comai−1,ai−jf_{i,j,suf(a_{i-1},j)}=f_{i-1,j,pre(a_i,j)}+com_{a_{i-1},a_i}-jfi,j,suf(ai−1,j)=fi−1,j,pre(ai,j)+comai−1,ai−j
时间复杂度O(n∗l)O(n*l)O(n∗l)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2e5+10,inf=2147483647/3;
int n,L,a[N],f[21][1<<21],ans;
char s[N];
int pre(int x,int i)
{return x>>(L-i);}
int suf(int x,int i)
{return x&((1<<i)-1);}
int com(int x,int y){for(int i=L;i>=0;i--)if(suf(x,i)==pre(y,i))return i;
}
int main()
{scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%s",s);if(i==1)L=strlen(s);for(int j=0;j<L;j++)a[i]=(a[i]<<1)+(s[j]-'0');}memset(f,0x3f,sizeof(f));f[0][0]=L;for(int i=2;i<=n;i++){int tmp=L-com(a[i-1],a[i]),mins=inf;ans+=tmp;for(int j=0;j<=L;j++)mins=min(mins,f[j][pre(a[i],j)]+L-j-tmp);for(int j=0;j<=L;j++)f[j][suf(a[i-1],j)]=min(f[j][suf(a[i-1],j)],mins);}printf("%d\n",f[0][0]+ans);
}