Compress Words CodeForces - 1200E、Test CodeForces - 25E、Password CodeForces - 126B (KMP)

Password CodeForces - 126B

題意:

給定一個字符串,求出一個子串滿足前綴,後綴,原字符串中間均有該字串

思路:

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;
}

Test CodeForces - 25E

題意:

給定三個字符串,求一個最短長度的字符串使得這三個字符串都是它的子串

思路:

利用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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章