ICPC NEAU Programming Contest 2020 A.盘他!题解

前序

由于比赛地址是在计蒜客,所以本篇题解省略题的详细地址,只给出比赛地址,对于题目有兴趣的可以点击下方链接
比赛地址

A. 盘他!

截图

ICPC NEAU Programming Contest 2020 A.盘他!题解_第1张图片

题目类型

字符串模拟

题意简述

求解子串在首尾可以无线相接的母串中不重叠出现k次,需要经过多少个字符(从头开始计算)

题解

本题的解答概括为两个部分,

  • 第一个部分是KMP匹配
  • 第二个部分是寻找循环节

kmp匹配的目的是为了判断是否有可能存在不重叠出现k次的情况,并且为寻找循环节和最后位置提供机会

这里先给出题中样例,并进行一些解释

输入样例

5 3 2
ououo ouo

输出样例

8

5 表示单一母串长度
3 表示子串长度
2 表示子串不重叠出现次数

经过头尾链接子串出现 2 次的情况是如下

ououo ououo

通过取余的方式解决头尾无限相接的问题,这样就可以正常的进行KMP匹配惹

代码

const int MAXN=2e5+50;
int nex[MAXN];
int next_start[MAXN];
int start[MAXN];
int first_visit[MAXN];
LL first_visit_ans[MAXN];
//获取母串的next数组
void get_next(string P){
	//memset(nex, 0, sizeof(nex));
	int len = P.size();
	nex[0] = -1;
	int i = 0, j = -1;
	while(i < len){
		if (j == -1 || P[i] == P[j]) nex[++i] = ++j;
		else j = nex[j];
	}
}
int KMP(string P, string T){
	int Plen = P.size();
	int Tlen = T.size();
	//cout << "Plen " << Plen << " " << "Tlen " << Tlen << '\n';
	int i = 0, j = 0;
	int sum = 0;
	//%Tlen就能够枚举所有的起始点,即使是超过了,也能回到想要的位置
	while(i - Plen < Tlen - 1){
		if (j == -1 || P[j] == T[i % Tlen]) i++, j++;
		else j = nex[j];
		if (j == Plen) start[sum++] = i - Tlen,j = nex[j];
		//这里是对寻找到的位置返回记录匹配成功的第一个点,通过start数组就能找到所有寻找到的匹配成功的位置
	}
	return sum; // 查找到的匹配成功的个数
}
int main(){
	int t; RD(t);
	FOR_1(e, 1, t){
		int n, m, k; RD(n, m, k);
		string s1, s2; cin >> s1 >> s2;
		get_next(s2);
		int num = KMP(s2, s1);
		int x, flag, round;LL ans = 0;
		if (num == 0) {
			OT("-1");
			continue;
		}//一个匹配都没找到
		for(int i = 0;i < s1.size(); i++){
			next_start[i]  = -1;
			first_visit[i] = -1;
		}
		for(int i = 0; i < num; i++){
			next_start[start[i]] = start[i];
		}
		x = -1;
		for(int i = n * 2 - 1; i >= 0; i--){
			if (next_start[i % n] != -1) x = next_start[i % n];
			else next_start[i % n] = x;
		}
		x = 0, ans = 0, flag = 0; //初始化
		for(LL i = 1; i <= k; i++){
			ans += (next_start[x] - x + n) % n;
            x = next_start[x];
            if(flag == 0)
            {
                if(first_visit[x] != -1)
                {
                    flag = 1;
                    round = i - first_visit[x]; //当前位置与出现位置的距离,即循环节
                    ans += ((k-i) / round) * (ans - first_visit_ans[x]);
                    i += (k-i)/round*round;
                }
                first_visit[x] = i;
                first_visit_ans[x] = ans;
            }
            ans += m;//完整匹配的距离即为子串的长度
            x = (x + m) % n;
        }
        printf("%lld\n",ans);
		}
}

你可能感兴趣的:(ICPC)