kmpABCソリューション

poj1961期間

N文字の特定の文字列Sの各プレフィックス(各文字のASCIIコードは97〜126)について、プレフィックスが周期的な文字列であるかどうかを知りたいと思います。つまり、各i(2 <= i <= N)について、最大のK> 1(存在する場合)を知りたいので、長さiのSの接頭辞をAK、つまりA連結Kと書くことができます。ある文字列Aについては、もちろん、期間Kも知りたいと思います
入力
入力は、いくつかのテストケースで構成されています。各テストケースは2行で構成されています。最初の行にはN(2 <= N <= 1 000 000)–文字列Sのサイズが含まれます。2行目には文字列Sが含まれます。入力ファイルは、
数字がゼロの行で終わります
出力
テストケースごとに、「テストケース番号」と連続するテストケース番号を1行で出力します。次に、ピリオドK> 1の長さiのプレフィックスごとに、プレフィックスサイズiとピリオドKを1つのスペースで区切って出力します。プレフィックスサイズは昇順である必要があります。各テストケースの後に空白行を印刷します。
サンプル入力
3
AAA
12
aabaabaabaab
0
サンプル出力
テストケース#1
2 2
3 3

テストケース#2
2 2
6 2
9 3
12 4

解決策:
質問意味は、aabaabaabaabのように、最初のi文字でループセクションの出現回数を見つけることです(ループセクションの数は1より大きい必要があります)。ここで、aは最初の2文字で2回出現します。文字であり、ループセクションはです。最初の6文字では、ループセクションが2回表示され、ループセクションはaabであり、最初の9文字のうち、ループセクションは3回表示され、ループセクションはaabです。12文字では、ループセクションは4回表示されます。ループセクションはaabです。主に次の配列の使用法を調べます。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 HDU3336文字列を数える

AekdyCoinは、数論の問題だけでなく、文字列の問題にも優れていることはよく知られています。文字列sを指定すると、この文字列の空でないプレフィックスをすべて書き留めることができます。次に例を示します
。s:「abab」
プレフィックスは次のとおりです。「a」、「ab」、「aba」、「abab」
プレフィックスごとに、sで一致する回数をカウントできます。したがって、接頭辞「a」が2回一致し、「ab」も2回一致し、「aba」が1回一致し、「abab」が1回一致することがわかります。ここで、すべてのプレフィックスの一致時間の合計を計算するように求められます。「abab」の場合、2 + 2 + 1 + 1 = 6です
。答えは非常に大きい可能性があるため、答えmod 10007を出力します。
入力
最初の行は、テストケースの数を示す単一の整数Tです。
いずれの場合も、最初の行は整数n(1 <= n <= 200000)であり、これは文字列sの長さです。文字列sを与える行が続きます。文字列内の文字はすべて小文字です。
出力
それぞれの場合について、出力つのみ番号:Sのすべてのプレフィックスに一致する時間の合計は、10007国防省
サンプル入力
1
4
ABAB
サンプル出力
6

問題の解決策問題の
意味は、文字列を提供し、文字列のすべてのプレフィックスが文字列自体と一致する回数の合計を見つけることです。たとえば、ababの接頭辞はa、ab、aba、ababです。次に、これらの4つのプレフィックスを使用してabab自体を照合します。2、2、1、および1の照合ポイントがあるため、合計は2 + 2 + 1 + 1 = 6になります。私の最初のアイデアは、ステップをカウントするためにansを定義することでした。次の配列は、プレフィックスとサフィックスの一致を記録します。一致する場合は、一致することを意味し、次にans ++を実行すると、文字列プレフィックスを1回一致させることができます。順番に、最後にansとnを足したものが答えです。合格することはできますが(バックグラウンドテストデータがごみすぎている可能性があります)、正しい解決策ではありません。たとえば、aaaaの例では、正の解の出力は10ですが、上記の方法で得られた答えは7です。自分でやっているときはあまり考えていなかったし、手渡した後も気にしませんでした。このバグが出るとは思っていませんでした。
正の解決策は、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;
}

CPOJ-2185ミルキンググリッド

毎朝搾乳されると、ファーマージョンの牛は、R(1 <= R <= 10,000)行×C(1 <= C <= 75)列の長方形のグリッドを形成します。ご存知のように、ファーマー・ジョンは牛の行動の専門家であり、現在、牛の摂食行動に関する本を書いています。彼は、各牛がその品種を示す大文字でラベル付けされている場合、搾乳中に牛によって形成される2次元パターンが、より小さな繰り返しの長方形パターンから作られているように見える場合があることに気付きます。

FJが、搾乳グリッド全体を構成するために繰り返し並べて表示できる最小領域の長方形の単位を見つけるのを手伝ってください。以下のサンプル入力に示されているように、小さな長方形のユニットの寸法は、必ずしも搾乳グリッド全体の寸法を均等に分割する必要はないことに注意してください。

入力

  • 1行目:2つのスペースで区切られた整数:RとC

  • 2行目…R + 1:牛が形成するグリッド。大文字は各牛の品種を示します。R個の各入力行にはC文字があり、スペースやその他の文字はありません。
    出力

  • 1行目:グリッドが形成される最小単位の領域
    サンプル入力
    2 5
    ABABA
    ABABA
    サンプル出力
    2
    ヒント
    搾乳グリッド全体は、パターン「AB」の繰り返しから構築できます。

解決策:
問題目的は、最小の文字行列領域を見つけることです。この行列は、この大きな行列でループできますが、すべてのループが同じであるとは限りません。同じ部分も、サンプルなどのループと見なされます。
具体的なアイデアは、それを2次元KMPとして扱い、各文字列を文字として取り、次の配列を見つけてから、ループ文字列の最小長、つまり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