简体   繁体   English

如何获得字符串的最短回文

[英]How to get the shortest palindrome of a string

For example : 例如 :

String is : abcd 字符串是:abcd

shortest palindrome is abcdcba is the solution 最短的回文是abcdcba是解决方案

longer palindrome can be : abcddcba 较长的回文可以是:abcddcba

another example: 另一个例子:

String : aaaab 字符串:aaaab

shortest palindrome is aaaabaaaa 最短的回文是aaaabaaaa

longer palindrome can be aaaaabbaaaa 较长的回文可以是aaaaabbaaaa

Restrictions : you can only add characters in the end. 限制:您最后只能添加字符。

Just append the reverse of initial substrings of the string, from shortest to longest, to the string until you have a palindrome. 只需将字符串的初始子串(从最短到最长)的反向追加到字符串,直到你有一个回文。 eg, for "acbab", try appending "a" which yields "acbaba", which is not a palindrome, then try appending "ac" reversed, yielding "acbabca" which is a palindrome. 例如,对于“acbab”,尝试附加“a”,其产生“acbaba”,这不是回文,然后尝试附加“ac”反转,产生“acbabca”,这是一个回文。

Update: Note that you don't have to actually do the append. 更新:请注意,您不必实际执行追加。 You know that the substring matches since you just reversed it. 你知道子串匹配,因为你只是颠倒了它。 So all you have to do is check whether the remainder of the string is a palindrome, and if so append the reverse of the substring. 因此,您所要做的就是检查字符串的其余部分是否为回文,如果是,则追加子字符串的反向。 Which is what Ptival wrote symbolically, so he should probably get the credit for the answer. 这是Ptival象征性地写的,所以他应该得到答案的功劳。 Example: for "acbab", find the longest suffix that is a palindrome; 示例:对于“acbab”,找到最长的后缀,即回文; that is "bab". 那就是“bab”。 Then append the remainder, "ac", in reverse: ac bab ca. 然后将剩余部分“ac”反向追加:ac bab ca.

My guess for the logic: 我对逻辑的猜测:

Say you string is [a1...an] (list of characters a1 to an) 假设你的字符串是[a1 ... an](字符a1到a的列表)

  • Find the smallest i such that [ai...an] is a palindrome. 找到最小的i,使得[ai ... an]是回文。
  • The smallest palindrome is [a1 ... a(i-1)] ++ [ai ... an] ++ [a(i-1) ... a1] 最小的回文是[a1 ... a(i-1)] ++ [ai ... an] ++ [a(i-1)... a1]

where ++ denotes string concatenation. 其中++表示字符串连接。

Some pseudo code, to leave at least a bit of work on you: 一些伪代码,至少给你一些工作:

def shortPalindrome(s):
  for i in range(len(s)):
    pal = s + reverse(s[0:i])
    if isPalindrome(pal):
      return pal
  error()

Python code, should be easy to convert to C: Python代码,应该很容易转换为C:

for i in range(1, len(a)):
    if a[i:] == a[i:][::-1]:
        break
print a + a[0:i][::-1]

I was also asked the same question recently, and here is what I wrote for my interview: 最近我也被问到了同样的问题,这就是我为我的采访所写的内容:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int isPalin ( char *str ) {
    int i, len=strlen(str);
    for (i=0; i<len/2; ++i)
        if (str[i]!=str[len-i-1])
            break;
    return i==len/2;
}

int main(int argc, char *argv[]) {
    if (argc!=2)
        puts("Usage: small_palin <string>");
    else {
        char *str = argv[1];
        int i=0, j, len=strlen(str);
        while ( !isPalin(str+i) )
            ++i;
        char *palin = malloc(len+1+i);
        *(palin+len+1+i) = '\0';
        strcpy(palin,str);
        for (i=i-1, j=0; i>=0; --i, ++j)
            *(palin+len+j) = str[i];
        puts(palin);
    }
    return 0;
}

I feel that the program would have been more structured had I written an strrev() function and checked palindrome using strcmp(). 如果我编写了strrev()函数并使用strcmp()检查了回文,我觉得该程序会更结构化。 This would enable me to reverse the starting characters of the source string and directly copy it using strcpy(). 这将使我能够反转源字符串的起始字符,并使用strcpy()直接复制它。

The reson why I went with this solution is that before this question I was asked to check for palindrome and I already had that isPalin() in paper. 为什么我选择这个解决方案的原因是,在这个问题之前我被要求检查回文并且我已经用纸张中的那个是帕林()。 Kind of felt using existing code would be better !! 使用现有代码的感觉会更好!!

Below is my answer for another case: shortest palindrome by attaching characters to the front. 下面是我对另一个案例的回答:通过将字符附加到前面来获得最短的回文。 So your task is to understand the algorithm and modify it appropriately. 所以你的任务是理解算法并适当地修改它。 Basically, it states that from a string s find the shortest palindrome by adding some characters to the front of s. 基本上,它声明从字符串s中通过在s的前面添加一些字符来找到最短的回文。

If you have never tried to solve this problem, I suggest that you solve it, and it will help you improve your problem solving skill. 如果您从未尝试解决此问题,我建议您解决它,它将帮助您提高解决问题的能力。

After solving it, I kept looking for better solutions. 解决之后,我一直在寻找更好的解决方案。 I stumbled upon another programmer's solution. 我偶然发现了另一个程序员的解决方案。 It is in python, and really neat. 它是在python中,非常整洁。 It is really interesting, but later I found out it was wrong. 这真的很有趣,但后来我发现这是错的。

class Solution:
    # @param {string} s
    # @return {string}
    def shortestPalindrome(self, s):
        A=s+s[::-1]
        cont=[0]
        for i in range(1,len(A)):
            index=cont[i-1]
            while(index>0 and A[index]!=A[i]):
                index=cont[index-1]
            cont.append(index+(1 if A[index]==A[i] else 0))
        print cont[-1]
        return s[cont[-1]:][::-1]+s

I myself looked at the Solution and saw it's interesting idea. 我自己看了解决方案,看到了这个有趣的想法。 At first, the algorithm concatenates the string and its reversed version. 首先,该算法连接字符串及其反转版本。 Then the following steps are similar to the steps for building KMP-table (or failure function) using in KMP algorithm. 然后,以下步骤类似于在KMP算法中使用构建KMP表(或失败函数)的步骤。 Why does this procedure work? 为什么这个程序有效?

If you know KMP text searching algorithm, you will know its "lookup table" and steps to build it. 如果您了解KMP文本搜索算法,您将了解其“查找表”以及构建它的步骤。 Right now, I just show one important use of the table: it can show you the longest prefix of a string s that is also suffix of s (but not s itself). 现在,我只是展示了表的一个重要用途:它可以显示字符串s的最长前缀,也是s的后缀(但不是s本身)。 For example, "abcdabc" has the longest prefix which is also a suffix: "abc" (not "abcdabc" since this is the entire string!!!). 例如,“abcdabc”具有最长的前缀,也是后缀:“abc”(不是“abcdabc”,因为这是整个字符串!!!)。 To make it fun, we call this prefix is "happy substring" of s. 为了让它变得有趣,我们称这个前缀是s的“happy substring”。 So the happy substring of "aaaaaaaaaa" (10 a's ) is "aaaaaaaaa" (9 a's). 因此,“aaaaaaaaa”(10 a's)的快乐子串是“aaaaaaaa”(9 a's)。

Now we go back and see how finding happy sub string of s can help solve the shortest palindrome problem. 现在我们回过头来看看如何找到快乐的子串s可以帮助解决最短的回文问题。

Suppose that q is the shortest string added to the front of s to make the string qs is a palindrome. 假设q是添加到s前面的最短字符串,以使字符串qs为回文结构。 We can see that obviously length(q) < length(s) since ss is also a palindrome. 我们可以看到,显然长度(q)<长度(s),因为ss也是回文。 Since qs is a palindrome, qs must end with q, or s = p+q where p is a sub string of s. 由于qs是回文,qs必须以q结尾,或s = p + q,其中p是s的子串。 Easily we see that p is also a palindrome. 很容易我们看到p也是回文。 Therefore, in order to have shortest qs, q needs to be shortest. 因此,为了具有最短的q,q需要最短。 In turn, p is the longest palindromic sub string of s. 反过来,p是s中最长的回文子串。

We call s' and q' are the reversed strings of s and q respectively. 我们称s'和q'分别是s和q的反向字符串。 We see that s = pq, s' = q'p since p is a palindrome. 我们看到s = pq,s'= q'p,因为p是回文。 So ss' = pqq'p . 所以ss'= pqq'p。 Now we need to find the longest p. 现在我们需要找到最长的p。 Eureka! 找到了! This also means that p is a happy sub string of the string ss'. 这也意味着p是字符串ss'的快乐子字符串。 That's how the above algorithm works!!! 这就是上述算法的工作方式!

However, after some thought, the above algorithm has some loophole. 但是,经过一番思考,上述算法存在一些漏洞。 p is not a happy sub string of ss'! p不是ss'的快乐子字符串! In fact, p is the longest prefix that is also a suffix of ss', but the prefix and suffix must not overlap each other. 实际上,p是最长的前缀,也是ss'的后缀,但前缀和后缀不能相互重叠。 So let's make it more fun, we call "extremely happy sub string" of a string s is the longest sub string of s that is a prefix and also a suffix and this prefix and suffix must not overlap. 所以让我们把它变得更有趣,我们称之为字符串的“非常幸福的子字符串”是s的最长子字符串,它是一个前缀,也是一个后缀,这个前缀和后缀不能重叠。 On the other word, the "extremely happy sub string" of s must have length less than or equal half length of s. 换句话说,s的“极其愉快的子串”的长度必须小于或等于s的一半长度。

So it turns out the "happy sub string" of ss' is not always "extremely happy sub string" of ss'. 所以事实证明,ss'的“快乐子字符串”并不总是“非常快乐的子字符串”的ss'。 We can easily construct an example: s = "aabba". 我们可以很容易地构造一个例子:s =“aabba”。 ss'="aabbaabbaa". SS' = “aabbaabbaa”。 The happy sub string of "aabbaabbaa" is "aabbaa", while the extremely happy sub string of "aabbaabbaa" is "aa". “aabbaabbaa”的快乐子字符串是“aabbaa”,而“aabbaabbaa”的非常快乐的子字符串是“aa”。 Bang! 砰!

Hence, the correct solution should be as following, based on the observation that length(p) <= length(ss')/2. 因此,基于长度(p)<=长度(ss')/ 2的观察,正确的解决方案应如下。

class Solution:
    # @param {string} s
    # @return {string}
    def shortestPalindrome(self, s):
        A=s+s[::-1]
        cont=[0]
        for i in range(1,len(A)):
            index=cont[i-1]
            while(index>0):
                if(A[index]==A[i]):
                    if index < len(s):
                        break
                index=cont[index-1]
            cont.append(index+(1 if A[index]==A[i] else 0))
        print cont[-1]
        return s[cont[-1]:][::-1]+s 

Hooray! 万岁! As you can see, algorithms are interesting! 如您所见,算法很有趣!

The link to the article I wrote here 我在这里写的文章的链接

It looks like the solutions outlined here are O(N^2) (for each suffix X of the reversed string S, find if S + X is a palindrome). 看起来这里概述的解是O(N ^ 2)(对于反向串S的每个后缀X,找到S + X是回文)。

I believe there is a linear, ie O(N) solution for this problem. 我相信这个问题有一个线性的,即O(N)解决方案。 Consider the following statement: the only time where you would append less characters than S.Length - 1 is when the string already contains a partial palindrome, so it will be in the form of NNNNNPPPPPP, where PPPPP represent a palindrome. 请考虑以下语句:唯一的时间是您添加的字符数少于S.Length - 1表示字符串已包含部分回文,因此它将采用NNNNNPPPPPP的形式,其中PPPPP表示回文。 This means that if we can find the largest trailing palindrome, we can solve it linearly by concatenating the reverse of NNNNN to the end. 这意味着如果我们能够找到最大的尾随回文,我们可以通过将NNNNN的反向连接到末尾来线性地求解它。

Finally, there exists a famous algorithm (Manacher, 1975) that finds the longest (and in fact, all) of the palindromes contained in a string (there is a good explanation here ). 最后,存在一个著名的算法(Manacher,1975)认为发现最长的(事实上,所有的)包含在字符串中的回文(有一个很好的解释在这里 )。 It can be easily modified to return the longest trailing palidrome, thus giving a linear solution for this problem. 它可以很容易地修改,以返回最长的尾随palidrome,从而为这个问题提供线性解决方案。

If anyone is interested, here is the full code for a mirror problem (append characters at the beginning): 如果有人有兴趣,这里是镜像问题的完整代码(在开头追加字符):

  using System.Text; // Via http://articles.leetcode.com/2011/11/longest-palindromic-substring-part-ii.html class Manacher { // Transform S into T. // For example, S = "abba", T = "^#a#b#b#a#$". // ^ and $ signs are sentinels appended to each end to avoid bounds checking private static string PreProcess(string s) { StringBuilder builder = new StringBuilder(); int n = s.Length; if (n == 0) return "^$"; builder.Append('^'); for (int i = 0; i < n; i++) { builder.Append('#'); builder.Append(s[i]); } builder.Append('#'); builder.Append('$'); return builder.ToString(); } // Modified to return only the longest palindrome that *starts* the string public static string LongestPalindrome(string s) { string T = PreProcess(s); int n = T.Length; int[] P = new int[n]; int C = 0, R = 0; for (int i = 1; i < n - 1; i++) { int i_mirror = 2 * C - i; // equals to i' = C - (iC) P[i] = (R > i) ? Math.Min(R - i, P[i_mirror]) : 0; // Attempt to expand palindrome centered at i while (T[i + 1 + P[i]] == T[i - 1 - P[i]]) P[i]++; // If palindrome centered at i expand past R, // adjust center based on expanded palindrome. if (i + P[i] > R) { C = i; R = i + P[i]; } } // Find the maximum element in P. int maxLen = 0; int centerIndex = 0; for (int i = 1; i < n - 1; i++) { if (P[i] > maxLen && i - 1 == P[i] /* the && part forces to only consider palindromes that start at the beginning*/) { maxLen = P[i]; centerIndex = i; } } return s.Substring((centerIndex - 1 - maxLen) / 2, maxLen); } } public class Solution { public string Reverse(string s) { StringBuilder result = new StringBuilder(); for (int i = s.Length - 1; i >= 0; i--) { result.Append(s[i]); } return result.ToString(); } public string ShortestPalindrome(string s) { string palindrome = Manacher.LongestPalindrome(s); string part = s.Substring(palindrome.Length); return Reverse(part) + palindrome + part; } } 

    using System;
    using System.Collections.Generic;
    using System.Linq;
    public class Test
    {

    public static void shortPalindrome(string [] words){
    List<string> container = new List<string>(); //List of Palindromes

    foreach (string word in words )
    {
        char[] chararray = word.ToCharArray();
        Array.Reverse(chararray);
        string newText = new string(chararray);

        if (word == newText) container.Add(word); 
    }

    string shortPal=container.ElementAt(0);
    for(int i=0; i<container.Count; i++)
    {
        if(container[i].Length < shortPal.Length){
            shortPal = container[i];
        }

    }

    Console.WriteLine(" The Shortest Palindrome is {0}",shortPal);

    }

    public static void Main()
    {
    string[] word = new string[5] {"noon", "racecar","redivider", "sun", "loss"};
    shortPalindrome(word);
    }
    }

From the examples you shown looks like the longest palindrome is the original string concatenated with its reverse, and the shortest is the original string concatenated with its reverse except for the first character. 从您显示的示例看起来最长的回文是与其反向连接的原始字符串,最短的是与其反向连接的原始字符串,除了第一个字符。 But I'm pretty sure you want something more complex. 但我很确定你想要更复杂的东西。 Perhaps you can give better examples? 也许你可以举出更好的例子?

如果字符串是由k个字符组成的,我认为你应该在这个字符串中添加反转(k-1)字符......

Shortest palindrome - 最短的回文 -

  • Reverse iterate from last positon + 1 to beginning 从最后一个位置+ 1开始反向迭代
  • Push_back the elements Push_back元素

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

int main()
{
    string str = "abcd" ;
    string shortStr = str ;
    for( string::reverse_iterator it = str.rbegin()+1; it != str.rend() ; ++it )
    {
        shortStr.push_back(*it) ;   
    }
    cout << shortStr << "\n" ;
}

And longer palindrome can be any longer. 更长的回文可以更长。

Ex: abcd 例如:abcd
Longer Palindrome - abcddcba, abcdddcba, ... 更长的回文 - abcddcba,abcdddcba,......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM