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 << '\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);
		}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章