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


    前序

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

    A. 盘他!

    截图

    在这里插入图片描述

    题目类型

    字符串模拟

    题意简述

    求解子串在首尾可以无线相接的母串中不重叠出现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 << '
    ';
    	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
    ",ans);
    		}
    }
    
  • 相关阅读:
    Codeforces 401C Team 贪心法
    C++ 编译,执行过程 具体解释。
    [从头学数学] 第156节 概率初步
    关于flex,好像有12个属性非常重要
    数据清洗小记(12):姓与名的提取
    理解Angular中的$apply()以及$digest()
    Oracle开发者守则
    黑马程序猿——25,打印流,合并流,对象序列化,管道流,RandomAccessFile
    GPU 编程入门到精通(四)之 GPU 程序优化
    Python 面向对象编程 继承 和多态
  • 原文地址:https://www.cnblogs.com/ygbrsf/p/13373617.html
Copyright © 2020-2023  润新知