SP7155 CF25E - Test

双倍经验:CF25E

前置知识:kmp,可左转 P3375

题意即求最短的使 s1,s2,s3s1,s2,s3 均为其子串的字符串 ss

很明显,字符串长度最大值就是三子串长度之和

然后考虑重合情况


化归:考虑两个子串,再考虑三个子串的情况

不妨先求最短的使 s1,s2s1,s2 均为其子串的字符串 ssss

两个子串仅有 33 种情况:(不妨假设 s1s1 的长度大于等于 s2s2,这里顺便借用一下两圆的关系)

  • 相离,例如 abccdabccdbddcbddc,此时 s=s1+s2s=s1+s2

  • 相交,例如 abccdabccdbccdebccde,此时 s=s1+s2s1s2s=s1+s2-s1\cap s2

  • 包含,例如 abccdabccdbccbcc,此时 s=s1s=s1

其中 s1s2s1\cap s2 拿 kmp 乱搞一下就行了,求匹配结束后 s2s2 的指针所在位置


三个子串怎么办?

可以先求 ssss,再求最短的使 ss,s3ss,s3 均为其子串的字符串,然后将 s2,s3s2,s3 互换,重来,再将 s1,s3s1,s3 互换,再来一次

也可以分多类讨论,也不难

最后献上分类讨论的代码:

#include<bits/stdc++.h>
#define inl inline 
#define rep(i,a,b) for(int i=(a),i##end=(b);i<=i##end;i++)
#define pre(i,a,b) for(int i=(a),i##end=(b);i>=i##end;i--)
using namespace std;
const int N=1e6+10;
int pmt[N][4];//pmt 数组,就可以看做是整体前移的 next 数组
void get_pmt(const string &s,int t){
	for(int i=1,j=0;i<s.size();++i){
		while(j&&s[i]!=s[j])j=pmt[j-1][t];
		if(s[i]==s[j])j++;
		pmt[i][t]=j;
	}
}
int kmp(string s,string p,int t1,int t2){
//	if(s.size()<p.size()){swap(s,p);swap(t1,t2);}
	int j=0;
	for(int i=0;i<s.size();i++){
		while(j&&s[i]!=p[j])j=pmt[j-1][t2];
		if(s[i]==p[j])j++;
		if(j==p.size())return -1;
	}
	return j;
}
string s[4];
int km[4][4];
signed main(void){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	while(cin>>s[1]>>s[2]>>s[3]){
		get_pmt(s[1],1);get_pmt(s[2],2);get_pmt(s[3],3);
		km[1][2]=kmp(s[1],s[2],1,2);
		km[2][1]=kmp(s[2],s[1],2,1);
		km[1][3]=kmp(s[1],s[3],1,3);
		km[3][1]=kmp(s[3],s[1],3,1);
		km[2][3]=kmp(s[2],s[3],2,3);
		km[3][2]=kmp(s[3],s[2],3,2);//这里注意:km[a][b]!=km[b][a],因为 km 第一维表示“文本串”,第二维则是“模式串”
		int ans=0x3f3f3f3f;
		rep(i,1,3)rep(j,1,3)rep(k,1,3){
		    if(i==j||j==k||i==k)continue; //只有两两不同才有意义
		    int sum=s[i].size()+s[j].size()+s[k].size()-km[i][j]-km[j][k]; //这里说下分多类讨论的写法
		    if(km[i][j]>=0&&km[j][k]>=0)ans=min(ans,sum); //如果不存在包含关系,那么 sum 即为所求
	        else{
	            if(km[i][j]<0&&km[i][k]<0)ans=min(ans,(int)s[i].size()); //存在一个大串使得其他两个小串均为其子串,那么大串长度即为所求
	            else if(km[i][j]<0)ans=min(ans,sum+km[i][j]+km[j][k]-(int)s[j].size()-km[i][k]);//sj 是 si 子串,那么不考虑 sj,化为两子串的问题 
	            if(km[j][k]<0)ans=min(ans,sum+km[j][k]-(int)s[k].size()); //sk 是 sj 子串,那么不考虑 sk,化为两子串的问题 
			}
		}
		cout<<ans<<'\n';
	}
}