双倍经验:SP7155,改成多组输入即可
前置知识:kmp,可左转 P3375
题意即求最短的使 均为其子串的字符串
很明显,字符串长度最大值就是三子串长度之和
然后考虑重合情况
化归:考虑两个子串,再考虑三个子串的情况
不妨先求最短的使 均为其子串的字符串
两个子串仅有 种情况:(不妨假设 的长度大于等于 ,这里顺便借用一下两圆的关系)
-
相离,例如 和 ,此时
-
相交,例如 和 ,此时
-
包含,例如 和 ,此时
其中 拿 kmp 乱搞一下就行了,求匹配结束后 的指针所在位置
三个子串怎么办?
可以先求 ,再求最短的使 均为其子串的字符串,然后将 互换,重来,再将 互换,再来一次
也可以分多类讨论,也不难
最后献上分类讨论的代码:
#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);
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';
}