Ausführliche Erklärung des Mancher-Algorithmus mit Übungsfragen

Mancher-Algorithmus

1. Einleitung

  • Es wird verwendet, um die Palindrom-Teilzeichenfolge zu berechnen, und ein Array wird verwendet, um den längsten Palindrom-Radius aufzuzeichnen, der nach beiden Seiten erweitert ist, wobei die aktuelle Position der Mittelpunkt ist

2. Ermitteln Sie die Länge des Palindrom-Teilstrings

Es gibt zwei Methoden:

Methode eins:

  • Brute-Force-Lösung ohne Optimierung, mit jeder Position als Mittelpunkt und Anpassung auf beiden Seiten.
  • Diese Methode weist einen Fehler auf: Sie kann nur ungerade Zahlen abgleichen. Wenn es sich beispielsweise um die beiden b in der Mitte abahandelt abba, erfolgt keine Übereinstimmung.
  • Die Lösung besteht darin, in der Mitte jedes Zeichens ein Sonderzeichen hinzuzufügen #. Jedes Zeichen ist in Ordnung, sogar die Zeichen, die in der Zeichenfolge vorhanden sind, sind ebenfalls möglich
  • abbaDann wird die Zeichenfolge zu #a#b#b#a#. Wenn Sie zu diesem Zeitpunkt auf beide Seiten erweitern, tritt kein Fehler auf

Methode Zwei:

  • Mancher-Algorithmus zum Ermitteln der Länge
  • Fügen Sie zunächst Sonderzeichen zur Originalzeichenfolge hinzu, um eine Mancher-Zeichenfolge zu erhalten
  • Verwenden Sie die Variablen C, R und ein Array arr, um die Länge des längsten Palindrom-Teilstrings zu ermitteln
  • Die Bedeutung der Variablen C: die Mittelposition des längsten Palindrom-Teilstrings
  • Die Bedeutung der Variablen R: die Position, an der die Übereinstimmung ganz rechts erfolgreich ist + 1
  • Die Bedeutung des arr-Arrays: die Länge des Palindroms an der aktuellen Position
  • In der for-Schleife i = 0gibt es vom Durchlauf bis zum Ende der Zeichenfolge vier Situationen :
    • Bevor Sie die 4 Fälle verstehen, müssen Sie einen wichtigen Punkt verstehen
    • Da C die mittlere Position ist, gibt es auf der linken und rechten Seite der Position, an der sich C befindet, einen großen Palindrombereich. left...rightDie iPosition liegt hinter der Position von C, sodass idie Position symmetrisch zur Position von C sein kann es wird erhalten i', das heißt, soi'...C...i
    • Fall 1: i > RDer Vertreter iist draußenR und kann nur gewaltsam gelöst werden
    • case2: i < RZeigt an , idass die entsprechende Position gemäß C gefunden und der Wert direkt übernommen wird, da im großen Bereich von C die linke Seite von C und die rechte Seite von C Palindrome sind, da sie symmetrisch sind . also der Wert Das Gleiche gilt für den Wert von und .Rii'arr[i']i'iarr[i']arr[i]
    • Fall 3: Wenn i < Rzu diesem Zeitpunkt die ientsprechende Position gemäß C gefunden wird . i'Wenn i'der Bereich von nicht mehr im Bereich des Palindroms von C liegt, arr[i']kann der Wert des Werts zu diesem Zeitpunkt nicht direkt erfasst werden, da er iinnerhalb liegt der Bereich des Palindroms von C, also idie längste Antwort. Die Textlänge kann nur die rechte Seite des C-Palindrombereichs sein, also Rarr[i] = R - i
    • Fall 4: Wenn i < Rzu diesem Zeitpunkt idie entsprechende Position gemäß C gefunden wird i'und i'die linke Seite von C links vom Palindrombereich von C gedrückt wird, arr[i']kann der Wert nicht direkt übernommen werden, aber zumindest eine bestimmte Palindromlänge kann ermittelt werden genommen werden, und Es ist der Palindrombereich auf der rechten Seite von C, also die Position von R, sodass arr[i]die Palindromlänge R-ivon Anfang an berechnet werden kann. Wenn später ein Palindrom angetroffen wird, nimmt die Länge weiter zu

3. Vollständiger Code

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;

string mancherString(string s) {
    
    
	string str = "#";
	for (int i = 0; i <= s.size(); i++) {
    
    
		str.push_back(s[i]);
		str.push_back('#');
	}
	return str;
}

int main() {
    
    
	string s = "abc1234321ab";
	string str = mancherString(s); // #a#b#c#1#2#3#4#3#2#1#a#b#
	vector<int> pArr(str.size(), 0);
	int C = -1; // 回文串的中心位置
	int R = -1; // 最右的扩成功位置,再下一个位置
	int Max = -1;
	for (int i = 0; i < str.size(); i++) {
    
    
		// i'...C...i
		// 当前位置是i  可以根据C对称的找到i' , 因为C所在的位置左右两侧有一个很大的回文区域left...right范围
		// case1: i'回文串的范围 在C回文串范围left和right内
		// 此时i可以直接取i'的值,因为i也在C的范围内,然后又是对称取的i'

		// case2: i'回文串的范围 在C回文范围left和right外
		// 因为i'的范围已经不在left和right内了,但是i在C的回文范围内,i最大长度也只能到C的right,也就是R
		// 此时直接出答案 i的值为R-i

		// case3: i'回文串的范围 左侧压到了C回文范围的left
		// 也就是说,i的最小长度是可以到C的right,当初C的left-1和right+1不是回文,只是因为这两个值不同
		// 但是有可能i的left-1位置和C的right+1位置相同,所以说得从right+1开始判断是否回文
		pArr[i] = R > i ? min(pArr[C * 2 - i], R - i) : 1; // C * 2 - i 就是 i'的位置
		
		// i - pArr[i]相当于left , i + pArr[i]相当于right
		// i - pArr[i] 左边最开始待匹配的位置 , i + pArr[i] 右边最开始待匹配的位置
		while (i - pArr[i] > -1 && i + pArr[i] < str.size()) {
    
    
			// 暴力扩,如果右侧第一个位置跟左侧第一个位置相等,代表回文长度需要加1,因为又是一个回文
			if (str[i - pArr[i]] == str[i + pArr[i]]) {
    
    
				pArr[i]++;
			}
			else {
    
    
				// 如果中了case1和case2会直接退出循环
				break;
			}
		}
		// 更新C和R
		if (i + pArr[i] > R) {
    
    
			R = i + pArr[i];
			C = i;
		}
		// 记录最大长度
		Max = max(Max, pArr[i]);
	}
	cout << "Ans :" << Max - 1 << endl; // Ans :7
	return 0;
}

4. Verwandte Themen zu Mancher

Für eine Zeichenfolge möchten wir die neue Zeichenfolge durch Hinzufügen von Zeichen zu einem Palindrom als Ganzes machen. Am Ende der ursprünglichen Zeichenfolge können jedoch nur Zeichen hinzugefügt werden. Bitte geben Sie die kürzeste am Ende hinzugefügte Zeichenfolge zurück.

Geben Sie bei gegebener Originalzeichenfolge A und ihrer Länge n die hinzugefügte Zeichenfolge zurück. Stellen Sie sicher, dass die Originalzeichenfolge kein Palindrom ist.

Testbeispiel:
„ab“, 2
ergibt: „a“


Der Titel ist der Kerncodemodus, und ich habe ihn als ACM-Modus geschrieben

Idee:
Die Bedeutung jeder Position im arr-Array im Mancher-Algorithmus: Nehmen Sie die aktuelle Position als Mittelpunkt, erweitern Sie sie nach beiden Seiten und bestimmen Sie die Länge des längsten Palindrom-Teilstrings. Diese Frage wird also von Mancher gelöst und die Bedeutung
der Die Frage besteht darin, am Ende der Zeichenfolge Zeichen hinzuzufügen. Sie müssen also beim Suchen des arr-Arrays nur die rechte Seite einer Position finden, die bis zum Ende der Zeichenfolge erweitert werden kann, die Länge speichern und dann die Schleife verlassen Durchlaufen Sie die in der ursprünglichen Zeichenfolge erhaltene Teilzeichenfolge und kehren Sie dann die Reihenfolge der
Teilzeichenfolge0位置原字符串的长度减去刚刚保存的长度加1

der Code

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;

string mancherString(string s) {
    
    
	string str = "#";
	for (int i = 0; i < s.size(); i++) {
    
    
		str.push_back(s[i]);
		str.push_back('#');
	}
	return str;
}

int process(string str) {
    
    
	int C = -1;
	int R = -1;
	int ans = 0;
	vector<int> pArr(str.size());
	for (int i = 0; i != str.size(); i++) {
    
    
		pArr[i] = R > i ? min(pArr[C * 2 - i], R - i) : 1;
		while (i + pArr[i] < str.size() && i - pArr[i] > -1) {
    
    
			if (str[i + pArr[i]] == str[i - pArr[i]]) {
    
    
				pArr[i]++;
			}
			else {
    
    
				break;
			}
		}
		if (i + pArr[i] > R) {
    
    
			C = i;
			R = i + pArr[i];
		}
		if (R == str.size()) {
    
    
			ans = pArr[i];
			break;
		}
	}
	return ans;
}

int main() {
    
    
	string s = "abbbbbaa"; 
	string str = mancherString(s);
	int len = process(str);
	cout << len;
	string ans = s.substr(0, s.size() - len + 1);
	reverse(ans.begin(), ans.end());
	cout << ans;//bbbbba
	return 0;
}

empfohlener Artikel

Ausführliche Erläuterung des KMP-Algorithmus mit Übungsfragen

Ich denke du magst

Origin blog.csdn.net/weixin_44839362/article/details/117135503
Empfohlen
Rangfolge