kmp A B C 题解

A poj 1961 Period

For each prefix of a given string S with N characters (each character has an ASCII code between 97 and 126, inclusive), we want to know whether the prefix is a periodic string. That is, for each i (2 <= i <= N) we want to know the largest K > 1 (if there is one) such that the prefix of S with length i can be written as AK ,that is A concatenated K times, for some string A. Of course, we also want to know the period K.
Input
The input consists of several test cases. Each test case consists of two lines. The first one contains N (2 <= N <= 1 000 000) – the size of the string S.The second line contains the string S. The input file ends with a line, having the
number zero on it.
Output
For each test case, output “Test case #” and the consecutive test case number on a single line; then, for each prefix with length i that has a period K > 1, output the prefix size i and the period K separated by a single space; the prefix sizes must be in increasing order. Print a blank line after each test case.
Sample Input
3
aaa
12
aabaabaabaab
0
Sample Output
Test case #1
2 2
3 3

Test case #2
2 2
6 2
9 3
12 4

题解:
题意是在前i个字符中,求循环节出现的次数(要求循环节出现的次数要大于1),比如aabaabaabaab,在前两个字符中a出现了两次,循环节为a。在前6个字符中,循环节出现了2次,循环节为aab,在前9个字符中,循环节出现了3次,循环节为aab;在12个字符中循环节出现了4次,循环节为aab。主要考察next数组的运用,i-next[i]为循环节长度
代码如下:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cctype>
#include<iomanip>
#include<map>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<set>
#include<cctype>
#include<string>
#include<stdexcept>
#include<fstream>
#define mem(a,b) memset(a,b,sizeof(a))
#define mod 10000007
#define debug() puts("what the fuck!!!")
#define ll long long
#define speed {
    
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); };
using namespace std;
const double PI = acos(-1.0);
const int maxn = 1e6 + 10;
const int INF = 0x3f3f3f3f;
const double esp_0 = 1e-6;
int gcd(int x, int y) {
    
    
	return y ? gcd(y, x % y) : x;
}
string s, p;
int Next[maxn];
int ans;

void getfail(string p) {
    
    
	Next[0] = -1;
	int k = -1;
	int j = 0;
	int plen = p.length();
	while (j < plen) {
    
    
		if (k == -1 || p[j] == p[k]) {
    
    
			j++;
			k++;
			//	if (p[j] != p[k])//kmp查找情况下next优化部分
			Next[j] = k;
			//	else
			//		Next[j] = Next[k];
		}
		else
			k = Next[k];
	}
}
int kmp(string s, string p) {
    
    
	int i = 0, j = 0;
	int plen = p.length();
	int slen = s.length();
	while (i < slen) {
    
    
		if (j == -1 || s[i] == p[j]) {
    
    
			i++;
			j++;
		}
		else
			j = Next[j];
		if (j == plen)
			return i = j;
	}
	return -1;
}
int main() {
    
    
	speed;
	string s, p;
	int n;
	int step = 1;
	while (cin >> n) {
    
    
		if (n == 0)break;
		cin >> p;
		getfail(p);
		cout << "Test case #" << step << endl;
		step++;
		for (int i = 0; i <= n; ++i) {
    
    //因为Next数组是前后缀最大公共元素长度整体右移一个单位的结果,所以要遍历到n
			//Next>0即存在前后缀公共字符,i-Next[i]为该节点前的循环节长度,i%(i-Next[i])==0即前后缀公共子串正好截成相同的几段
			if (Next[i] > 0 && i % (i - Next[i]) == 0)cout << i << " " << i / (i - Next[i]) << endl;
		}
		cout << endl;
	}
	return 0;
}

B HDU 3336 Count the string

It is well known that AekdyCoin is good at string problems as well as number theory problems. When given a string s, we can write down all the non-empty prefixes of this string. For example:
s: “abab”
The prefixes are: “a”, “ab”, “aba”, “abab”
For each prefix, we can count the times it matches in s. So we can see that prefix “a” matches twice, “ab” matches twice too, “aba” matches once, and “abab” matches once. Now you are asked to calculate the sum of the match times for all the prefixes. For “abab”, it is 2 + 2 + 1 + 1 = 6.
The answer may be very large, so output the answer mod 10007.
Input
The first line is a single integer T, indicating the number of test cases.
For each case, the first line is an integer n (1 <= n <= 200000), which is the length of string s. A line follows giving the string s. The characters in the strings are all lower-case letters.
Output
For each case, output only one number: the sum of the match times for all the prefixes of s mod 10007.
Sample Input
1
4
abab
Sample Output
6

题解
题意是 给你一个串,求用该串所有前缀去 匹配本身这个串的次数 的总和。比如串abab,它的前缀有a,ab,aba,abab。那么拿这4个前缀去匹配abab自身分别有2,2,1,1个匹配点,所以总和为2+2+1+1=6。我一开始的想法是定义ans来计步,next数组记录下前后缀匹配情况,如果存在匹配情况,说明存在匹配,则ans++,然后字符串前缀依次可匹配一次,所以最后ans再加上n即为答案。虽然交了也能过(可能后台测试数据太垃圾了),但不是正解。比如aaaa这个样例,正解输出是10,但是上面的方法得出的答案是7。当时自己做的时候也没想这么多,交了过了就没管了,没想到还有这bug。
正解应该是要用dp+next数组做的,当i匹配失败的时候会回到next[i]的位置,所以当前串包含了next[i]所包含的前缀数+其当前本身,所以可以得到状态转移方程:dp[i] = dp[ next[i] ] +1
代码如下:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cctype>
#include<iomanip>
#include<map>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<set>
#include<cctype>
#include<string>
#include<stdexcept>
#include<fstream>
#define mem(a,b) memset(a,b,sizeof(a))
#define mod 10000007
#define debug() puts("what the fuck!!!")
#define ll long long
#define speed {
    
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); };
using namespace std;
const double PI = acos(-1.0);
const int maxn = 2e5 + 10;
const int INF = 0x3f3f3f3f;
const double esp_0 = 1e-6;
int gcd(int x, int y) {
    
    
	return y ? gcd(y, x % y) : x;
}
string s, p;
int Next[maxn];
int ans;
int dp[maxn];
char str[maxn];
void getFail(char* str) {
    
    
	Next[0] = -1;
	int j = 0;
	int k = -1;
	int plen = strlen(str);
	while (j < plen) {
    
    
		if (k == -1 || str[j] == str[k]) {
    
    
			k++;
			j++;
			Next[j] = k;
		}
		else
			k = Next[k];
	}
}
int main() {
    
    
	int t;
	cin >> t;
	while (t--) {
    
    
		int n;
		scanf("%d", &n);
		scanf("%s", &str);
		getFail(str);
		int ans = 0;
		for (int i = 1; i <= n; i++)//当前串包含了next[i]所包含的前缀数+其当前本身
			dp[i] = (dp[Next[i]] + 1) % 10007, ans = (dp[i] + ans) % 10007;
		printf("%d\n", ans);
	}
	return 0;
}

C POJ - 2185 Milking Grid

Every morning when they are milked, the Farmer John’s cows form a rectangular grid that is R (1 <= R <= 10,000) rows by C (1 <= C <= 75) columns. As we all know, Farmer John is quite the expert on cow behavior, and is currently writing a book about feeding behavior in cows. He notices that if each cow is labeled with an uppercase letter indicating its breed, the two-dimensional pattern formed by his cows during milking sometimes seems to be made from smaller repeating rectangular patterns.

Help FJ find the rectangular unit of smallest area that can be repetitively tiled to make up the entire milking grid. Note that the dimensions of the small rectangular unit do not necessarily need to divide evenly the dimensions of the entire milking grid, as indicated in the sample input below.

Input

  • Line 1: Two space-separated integers: R and C

  • Lines 2…R+1: The grid that the cows form, with an uppercase letter denoting each cow’s breed. Each of the R input lines has C characters with no space or other intervening character.
    Output

  • Line 1: The area of the smallest unit from which the grid is formed
    Sample Input
    2 5
    ABABA
    ABABA
    Sample Output
    2
    Hint
    The entire milking grid can be constructed from repetitions of the pattern ‘AB’.

题解:
题意就是让我们求出最小的字符矩阵面积,这个矩阵是这个大矩阵里能够循环的,但是并不一定是全部循环相同的,部分相同也算循环,比如样例。
具体思路就是把它当作二维的KMP做,把每一个字符串当做字符进行求next数组,然后求出最小的循环字符串长度,即:len-next[len]。
因为要求这个循环矩阵的长和宽,所以长就是每一行作为一个字符串,然后求出最小循环字符串长度;宽就是每一列作为一个字符串,也求出循环长度,相乘即为答案。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cctype>
#include<iomanip>
#include<map>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<set>
#include<cctype>
#include<string>
#include<stdexcept>
#include<fstream>
#define mem(a,b) memset(a,b,sizeof(a))
#define mod 10000007
#define debug() puts("what the fuck!")
#define dedebug() puts("what the fuck!!!")
#define ll long long
#define ull unsigned long long
#define speed {
    
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); };
using namespace std;
const double PI = acos(-1.0);
const int maxn = 1e4 + 50;
const int INF = 0x3f3f3f3f;
const double esp_0 = 1e-6;
int gcd(int x, int y) {
    
    
	return y ? gcd(y, x % y) : x;
}
int Next[maxn];
char s[maxn][maxn], str[80][maxn], ss[maxn];
void getnexts(int plen, char p[][maxn]) {
    
    
	Next[0] = -1;
	int k = -1;
	int j = 0;
	while (j < plen) {
    
    
		if (k == -1 || strcmp(p[k], p[j]) == 0) {
    
    //将字符串当作字符来处理
			k++;
			j++;
			Next[j] = k;
		}
		else
			k = Next[k];
	}
}
int main() {
    
    
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i = 0; i < n; i++)
		scanf("%s", s[i]);
	for (int i = 0; i < m; i++)
		for (int j = 0; j < n; j++)
			str[i][j] = s[j][i];
	mem(Next, 0);
	getnexts(n, s);
	int ans1 = n - Next[n];//求出循环节长度
	//for (int i = 0; i <= n; ++i)cout << Next[i] << " ";
	//cout << endl;
	mem(Next, 0);
	getnexts(m, str);
	int ans2 = m - Next[m];//求出循环节长度
	//for (int i = 0; i <= m; ++i)cout << Next[i] << " ";
	//cout << endl;
	printf("%d", ans1 * ans2);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40924271/article/details/107599095