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

posted @ 2019-09-02 10:58  xzhws  阅读(51)  评论(0编辑  收藏  举报