蓝桥杯2024年第十五届省赛真题-吊坠

题目描述
小蓝想制作一个吊坠,他手上有\(n\)个长度为\(m\)的首尾相连的环形字符串\(\{s_1, s_2,..., s_n\}\),他想用\(n−1\)条边将这\(n\)个字符串连接起来做成吊坠,要求所有的字符串连完后形成一个整体。连接两个字符串\(s_i\),\(s_j\)的边的边权为这两个字符串的最长公共子串的长度(可以按环形旋转改变起始位置,但不能翻转),小蓝希望连完后的这\(n−1\)条边的边权和最大,这样的吊坠他觉得最好看,请计算最大的边权和是多少。

输入格式
输入的第一行包含两个正整数\(n, m\),用一个空格分隔。
接下来\(n\)行,每行包含一个长度为\(m\)的字符串,分别表示\(s_1, s_2,...,s_n\)

输出格式
输出一行包含一个整数表示答案。

样例输入
4 4
aabb
abba
acca
abcd
样例输出
8
提示
样例说明
连接\(<1,2>, <2,3>,<2,4>\),边权和为 4 + 2 + 2 = 8
【评测用例规模与约定】
对于 20% 的评测用例,1 ≤ n, m ≤ 10;
对于所有评测用例,1 ≤ n ≤ 200 ,1 ≤ m ≤ 50。所有字符串由小写英文字母组成。

这个题首先先解决一个问题:就是可旋转的两个字符串,怎么求它的最长公共子串的长度?
字符串可旋转,考虑在每个字符串后重复一次自身,如 "abcd" 变为 "abcdabcd",这样在用dp就可以了。
然后对于任意两个[i,j],求出他的最长公共字串。然后就转化成[i,j,w]。i,j是边,w是权值。
然后用 kruskal 算法求最大生成树。

有一个要注意,那个结构体数组要开大一点,wa了两遍。

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m;
const int maxn=1e3+100;
int dp[maxn][maxn];
char a[maxn][maxn];
struct node{
	int x,y,w;
}t[100100];
int pre[maxn];
int find(int x){
	if(pre[x]==x){
		return pre[x];
	}
	return pre[x]=find(pre[x]);
}
int cmp(node x,node y){
	return x.w>y.w;
}
int get(char c[],char d[]){
	int ma=0;
	for(int i=1;i<=2*m;i++){
		for(int j=1;j<=2*m;j++){
			if(c[i]==d[j]){
				dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
			}
			ma=max(ma,dp[i][j]);
		}
	}
	for(int i=1;i<=2*m;i++){
		for(int j=1;j<=2*m;j++){
			dp[i][j]=0;
		}
	}
	return min(ma,m);
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		pre[i]=i;
	} 
	for(int i=1;i<=n;i++){
		scanf("%s",a[i]+1);
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			a[i][m+j]=a[i][j];
		}
	}
	int cnt=0;
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			cnt++;
			t[cnt].x=i,t[cnt].y=j,t[cnt].w=get(a[i],a[j]);
		}
	}
	sort(t+1,t+cnt+1,cmp);
	int ans=0;
	for(int i=1;i<=cnt;i++){
		int xx=find(t[i].x);
		int yy=find(t[i].y);
		if(xx!=yy){
			ans+=t[i].w;
			pre[xx]=yy;	
		}
	}
	cout<<ans<<endl;
	return 0; 
}
posted @ 2024-05-11 23:31  lipu123  阅读(1150)  评论(0)    收藏  举报