Original question:
Given a stringS, Find the longest palindromic substring inS. You may assume that the maximum lengthSIs 1000, and there exists one unique longest palindromic substring.
Question Analysis:
The longest response string. Is to give a string S, find the longest echo substring, and return the substring.
Solution:
The first method is the cyclic brute-force enumeration. The complexity is O (n ^ 3), but it should time out, so I didn't try it.
The second method is to delete multiple repeated statements in the brute-force solution. It is easy to think of dynamic planning, time complexity O (N ^ 2), Space O (N ^ 2). The dynamic planning equation is as follows:
- DP [I] [J] indicates the substring s [I... J] whether it is a reply
- Initialization: DP [I] [I] = true (0 <= I <= N-1); DP [I] [I-1] = true (1 <= I <= N-1 ); other initialization values are false.
- DP [I] [J] = (s [I] = s [J] & DP [I + 1] [J-1] = true)
Save the maximum length and start point of the retrieval in the dynamic plan.
Class solution {public: String longestpalindrome (string s) {const int Len = S. size (); If (LEN <= 1) return s; bool DP [Len] [Len]; // DP [I] [J] indicates s [I .. j] whether it is a background memset (DP, 0, sizeof (DP); int resleft = 0, resright = 0; DP [0] [0] = true; for (INT I = 1; I <Len; I ++) {DP [I] [I] = true; DP [I] [I-1] = true; // This Initialization is easy to ignore.} For (int K = 2; k <= Len; k ++) is used when k = 2) // enumerate the length of the substring for (INT I = 0; I <= len-K; I ++) // enumerate the starting position of the substring {If (s [I] = s [I + k-1] & DP [I + 1] [I + K-2]) {DP [I] [I + k-1] = true; If (resright-resleft + 1 <k) {resleft = I; resright = I + k-1;} return S. substr (resleft, resright-resleft + 1 );}};
Solution 3: The Center extension method, which is easy to think.
Take an element as the center, calculate the maximum length of the even-number-length and the maximum length of the odd-number-length. Time complexity O (N ^ 2), Space O (1)
Class solution {public: String longestpalindrome (string s) {const int Len = S. size (); If (LEN <= 1) return s; int start, maxlen = 0; For (INT I = 1; I <Len; I ++) {// look for a return int low = I-1, high = I with an even point length in the I-1; while (low> = 0 & high <Len & S [low] = s [High]) {LOW --; high ++ ;} if (high-low-1> maxlen) {maxlen = high-low-1; Start = low + 1 ;} // search for input with an odd number of I-centered characters: Low = I-1; high = I + 1; while (low> = 0 & high <Len & S [low] = s [High]) {LOW --; high ++ ;} if (high-low-1> maxlen) {maxlen = high-low-1; Start = low + 1 ;}} return S. substr (START, maxlen );}};
The fourth method is a very powerful algorithm. I did not expect this to happen...
Manacher algorithm, time complexity O (N), space complexity O (N)
This algorithm first pre-processes the string and adds a special symbol before and after each character of the string. For example, the string ABCD is processed as # A # B # C # D #. In order to avoid cross-border processing, add two special characters at the beginning and end of the string (C-type strings do not need to be added at the end, because they come with '\ 0 '), in this way, the preprocessing will eventually become $ # A # B # C # D # ^, after such processing, there is a benefit that the original even length and the odd length of the retrieval are all odd length in the processed string. Assume that the processed string is S.
For pre-processed strings, we use the array P [I] to record the maximum length of the string that is centered on the Character s [I] to expand to the left/right (including s [I] ), take the string "12212321" as an example. The P array is as follows:
S: $#1 #2 #2 #1 #2 #3 #2 #1 # ^
P: 1 2 1 2 5 2 1 1 1 2 1 6 1 2 1
As you can see,P [I]-1 is exactly the total length of the input string in the original string,If the P array is known, traverse the P array and find the maximum P [I] To find the maximum length of the retrieval and the location of the retrieval.
The following describes how to evaluate the P [] array:
Set the ID to the center of the longest string retrieved. mx is the right boundary of the longest string retrieved (excluding the right boundary), that isMX = ID + P [ID].J = 2 * ID-I, that is, J is the symmetry point of ID.
1When I <MX, such. At this time, we can draw a very magical conclusion.P [I]> = min (P [2 * ID-I], Mx-I ),Let's explain this conclusion.
How to calculate P [I] Based on P [J] is further divided into two situations:
(1.1) When Mx-I> P [J], the string centered on s [J] is contained in the string centered on s [ID, because I and j are symmetric, the string centered on s [I] must be contained in the string centered on s [ID, therefore, P [I] must at least be equal to P [J], and the subsequent matching will continue. For example
Note: In fact, P [I] must be equal to P [J], and no matching is required. Because if p [I] can continue to match, P [J] can continue to expand according to the symmetry.
(1.2) When Mx-I <= P [J], the substring centered on s [J] is not completely contained in the string centered on s [ID]. However, based on symmetry, we can see that, the two green boxes in are the same, that is, the substring centered on s [I] will expand to at least the MX position to the right, that is to say, P [I] is at least equal to Mx-I. If the parts after Mx are symmetric, they can only be honestly matched.
NOTE: If Mx-I <p [J], p [I] must be equal to Mx-I, because if p [I] can continue matching after MX, according to the symmetry, the point (including MX) matched after MX will appear before my, which means that P [ID] can be expanded.
2When I> = Mx, no more assumptions can be made for P [I]. Only P [I] = 1 can be used for matching.
Algorithm complexity analysis: according to the comments in italic, only Mx-I = P [J] And I> MX need to be expanded and compared, while MX is constantly expanding, in general, each element is a linear relationship of N, so the time complexity is O (n)
Class solution {public: String longestpalindrome (string s) {const int Len = S. size (); If (LEN <= 1) return s; // manncher algorithm, O (n) string STR = preprocess (s); int n = Str. size (), id = 0, MX = 0; vector <int> P (n, 0); For (INT I = 1; I <n-1; I ++) {P [I] = Mx> I? Min (P [2 * ID-I], Mx-I): 1; // If (MX <= I | (MX> I & P [2 * ID-I] = Mx-I) // Based on the comment in italic text, here, do you want to while (STR [I + P [I] = STR [I-P [I]) P [I] ++; if (I + P [I]> MX) {MX = I + P [I]; id = I ;}// traverse P, max retrieval length int maxlen = 0, Index = 0; For (INT I = 1; I <n-1; I ++) if (P [I]> maxlen) {maxlen = P [I]; Index = I;} return S. substr (index-maxlen)/2, maxLen-1);} // preprocessing string, after ABC preprocessing, it becomes $ # A # B # C # ^ string preprocess (const string & S) {int n = S. size (); string res; Res. push_back ('$'); // place $ in the string header res. push_back ('#'); // use # As the interval of each character in the original string for (INT I = 0; I <n; I ++) {res. push_back (s [I]); Res. push_back ('#');} res. push_back ('^'); // return res with ^ as the end of the string ;}};