題意:
給定一個字符串,求出一個子串滿足前綴,後綴,原字符串中間均有該字串
思路:
KMP next數組的含義
next[i]表示前i個字符串中公共真前綴和真後綴的長度,
利用p[]表示next數組
首先嚐試p[sz],sz爲輸入字符串的長度,如果p[sz]的長度爲0,直接輸出“Just a legend”
否則嘗試p[p[sz]],意思是前綴中的公共前綴後綴長度,如果p[p[sz]]爲0直接輸出即可
…
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<set>
#include<queue>
#include<vector>
using namespace std;
#define N 1000010
int p[N];
int sz;
char s[N];
int ha[N];//標記前綴出現的次數
void kmp(char t[]){
int j=0;
p[1]=0;
for(int i=2;i<=sz;i++){
while(j>0 && t[i]!=t[j+1]) j=p[j];
if(t[i]==t[j+1]) j++;
p[i]=j;
}
}
int main(){
scanf("%s",s+1);
sz=strlen(s+1);
kmp(s);
for(int i=1;i<sz;i++) ha[p[i]]++;
int pos=p[sz];
while(pos){
if(ha[pos]){
for(int i=1;i<=pos;i++) printf("%c",s[i]);
printf("\n");
return 0;
}
else{
pos=p[pos];
}
}
printf("Just a legend\n");
}
Compress Words CodeForces - 1200E
題意:
給定一行字符串,將其中相鄰的有共同前後綴的子串進行合併,求和合並後最短的字符串
思路:
KMP
設第一個字符串爲a,第二個字符串爲b,如果a+b能夠進行壓縮,實際求b+a的最長的border(最長的公共前後綴長度),利用KMP可以做到這一點
每次把公共部分的前後綴長度求出後,將上一次的結果+除去前綴的當前字符串作爲合併後新的字符串,重複n次即可
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<set>
#include<queue>
#include<vector>
#include<string>
using namespace std;
#define N 1000010
string t,ans;
int p[N];
int cal(string s){ //s的公共真前綴和真後綴
int j=-1;
p[0]=-1;
int len=s.size();
for(int i=1;i<len;i++){
while(j>=0 && s[i]!= s[j+1]) j=p[j];
if(s[i]==s[j+1]) j++;
p[i]=j;
}
return p[len-1]+1;
}
int main(){
// freopen("p.txt","r",stdin);
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>t;
if(i==1) ans=t;
else{
int len1=t.size(),len2=ans.size();
int minv=min(len1,len2);
string tmp=t;
tmp+='#';
tmp.insert(tmp.end(),ans.end()-minv,ans.end());//tmp前綴
int cnt=cal(tmp);
ans.insert(ans.end(),t.begin()+cnt,t.end());
}
}
cout<<ans<<endl;
return 0;
}
題意:
給定三個字符串,求一個最短長度的字符串使得這三個字符串都是它的子串
思路:
利用dp[i][j]表示第i個子串+第j個子串中含有的最長公共前後綴的長度,KMP可以做到這一點
枚舉三個字符串組合的所有情況,更新最短長度即可
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<set>
#include<queue>
#include<vector>
using namespace std;
#define N 100010
int len[4];
string s[4];
int dp[4][4];
int p[N*3];
int kmp(string t,int length){
int j=-1;
p[0]=-1;
int sz=t.size();
for(int i=1;i<sz;i++){
while(j>=0 && t[i]!=t[j+1]) j=p[j];
if(t[i]==t[j+1]) j++;
p[i]=j;//前i個位置含有的公共真前綴和真後綴的長度
if(j==length-1) return length;
}
return p[sz-1]+1;
}
int main(){
// freopen("p.txt","r",stdin);
for(int i=1;i<=3;i++) cin>>s[i],len[i]=s[i].size();
for(int i=1;i<=3;i++){
for(int j=1;j<=3;j++){
if(i!=j){
string tmp=s[i];
tmp+='#';
tmp.insert(tmp.end(),s[j].begin(),s[j].end());
dp[i][j]=kmp(tmp,len[i]);
}
}
}
int ans=1e7;
for(int i=1;i<=3;i++){
for(int j=1;j<=3;j++) if(i!=j){
for(int k=1;k<=3;k++) if(i!=k && j!=k){
int length=len[i]+len[j]+len[k]-dp[j][i];
if(dp[j][i]==len[j]){
length-=dp[k][i];
}
else{
length-=dp[k][j];
}
ans=min(ans,length);
}
}
}
printf("%d\n",ans);
return 0;
}