如何将字符串分成尽可能少的回文?

时间:2010-10-24 13:59:55

标签: algorithm

这是一个interview question:“你给了一个字符串,你想把它分成尽可能少的字符串,这样每个字符串都是一个回文”。 (我猜一个字符串被认为是回文,即“abc”被分成“a”,“b”,“c”。)

你会怎么回答?

5 个答案:

答案 0 :(得分:4)

首先找到字符串中的所有回文,使得L [i] [j]表示在S [i]处结束的第j个最长回文的长度。让我们说S是输入字符串。这可以在O(N ^ 2)时间内完成,首先考虑长度为1的回文,然后是长度为2的回文,依此类推。 在你知道所有长度的i-2回文之后找到长度的回文是单个字符比较的问题。

此后这是一个动态编程问题。设A [i]表示子串(S,0,i-1)可以分解成的最小回文数。

A[i+1] = min_{0 <= j < length(L[i])} A[i - L[i][j]] + 1;

根据Micron的要求进行编辑: 这是计算L [i] [j]背后的想法。我只是写了这个来传达这个想法,代码可能有问题。

// Every single char is palindrome so L[i][0] = 1;
vector<vector<int> > L(S.length(), vector<int>(1,1));

for (i = 0; i < S.length(); i++) {
 for (j = 2; j < S.length; j++) {
   if (i - j + 1 >= 0 && S[i] == S[i-j + 1]) {
     // See if there was a palindrome of length j - 2 ending at S[i-1]
     bool inner_palindrome = false;
     if (j ==2) {
      inner_palindrome = true;
     } else {
       int k = L[i-1].length;
       if (L[i-1][k-1] == j-2 || (k >= 2 && L[i-1][k-2] == j-2)) {
         inner_palindrome = true;
       }
     }
     if (inner_palindrome) {
       L[i].push_back(j);
     }
   } 
 }
} 

答案 1 :(得分:2)

你可以在O(n ^ 2)时间内使用Rabin-Karp指纹识别来预处理字符串,以便在O(n ^ 2)时间内找到所有的回文。在预处理之后,您运行类似于以下代码:

np(string s) {
  int a[s.size() + 1];
  a[s.size()] = 0;
  for (int i = s.size() - 1; i >= 0; i--) {
    a[i] = s.size() - i;
    for (int j = i + 1; j <= s.size(); j++) {
      if (is_palindrome(substr(s, i, j))) // test costs O(1) after preprocessing
        a[i] = min(a[i], 1 + a[j]);
  }
  return a[0];
}

答案 2 :(得分:1)

一个等效问题是计算字符串的Snip数。

假设您想使用最少数量的剪切来剪切字符串,以便每个剩余部分本身都是一个回文。我们将这种剪切的数量称为字符串的剪切数。也就是说,剪辑数总是等于给定字符串中最小数量的回文数。每个长度为n的字符串的剪辑编号最多为n-1,每个回文都有剪辑编号0.这是工作的python代码。

def snip_number(str):
    n=len(str)
 
 #initialize Opt Table
 # Opt[i,j] = min number of snips in the substring str[i...j]
 
    Opt=[[0 for i in range(n)] for j in range(n) ]
 
 #Opt of single char is 0
    for i in range(n):
     Opt[i][i] = 0
 
 #Opt for adjacent chars is 1 if different, 0 otherwise
    for i in range(n-1):
     Opt[i][i+1]= 1 if str[i]!=str[i+1] else 0
 
 
# we now define sil as (s)substring (i)interval (l) length of the
# interval [i,j] --- sil=(j-i +1) and j = i+sil-1
 
# we compute Opt table entry for each sil length and
# starting index i
 
    for sil in range(3, n+1):
     for i in range(n-sil+1):
       j = i+sil-1
       if (str[i] == str[j] and Opt[i+1][j-1]==0):
         Opt[i][j] = 0
       else:
         snip= min( [(Opt[i][t]+ Opt[t+1][j] + 1 ) for t in range(i,j-1)])
         Opt[i][j] = snip

    return Opt[0][len(str)-1]
#end function snip_number()
mystr=[""for i in range(4)]         
mystr[0]="abc"
mystr[1]="ohiho"
mystr[2]="cabacdbabdc"
mystr[3]="amanaplanacanalpanama aibohphobia "


for i in range(4):
     print mystr[i], "has snip number:", snip_number(mystr[i])
     
# abc has snip number: 2
# ohiho has snip number: 0
# cabacdbabdc has snip number: 2
# amanaplanacanalpanama aibohphobia  has snip number: 1

答案 3 :(得分:0)

bool ispalindrome(string inp)
{
    if(inp == "" || inp.length() == 1)
    {
        return true;
    }
    string rev = inp;

    reverse(rev.begin(), rev.end());

    return (rev == inp);
}

int minsplit_count(string inp)
{
    if(ispalindrome(inp))
    {
        return 0;
    }

    int count= inp.length();

    for(int i = 1; i < inp.length(); i++)
    {
        count = min(count, 
                      minsplit_count(inp.substr(0, i))              + 
                      minsplit_count(inp.substr(i, inp.size() - i)) + 
                      1);
    }

    return count;
}

答案 4 :(得分:-2)

O(n ^ 3)溶液。递归迭代字符串。每封信用这封信作为回文的起点建立每个回文。注意奇数和偶数的回文。重复直到字符串结束。如果在字符串的末尾,回文数是最小的,那么记住你是如何到达那里的。如果当前回文总数的计数和字符串中剩余字母的数量大于当前回文数最小值,则不要进一步迭代。

优化:当从字符串末尾开始发现回文并搜索当前字母的出现时。测试子串到“palindromness”。不要从最短的回文开始,这不是最佳的。