如何有效地获取字符串字符的最小数字删除,以将其转换为回文

时间:2016-07-14 03:31:36

标签: c algorithm

关于问题how to convert a string to palindrome with minimum number of removals of characters of the string?。我编写程序来测试接受的答案。但递归需要太多时间。如何解决或改善这个问题?以下是接受的答案:

  

设dp [i,j] =将子串[i,j]转换为回文所需的最小删除次数。我们有:

     

dp[i, i] = 0 for all i (every single character is a palindrome)   至   找到dp [i,j],让我们考虑一个随机字符串。我们有两个   可能性:

     
      
  1. 第一个和最后一个字符相等:a [i] == a [j]。在这种情况下,   我们可以将问题减少到找到最小数量的字符   需要删除才能使子串[i + 1,j-1]成为a   回文。

  2.   
  3. 第一个和最后一个字符不相等:a [i]!= a [j]。   在这种情况下,我们需要删除其中一个。我们将删除那个   引导我们找到更好的解决方案。

  4.   
/* remvoe the least characters to make a string be palindrome */

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

#define MAXLINE  4096

int func(char *p, int low, int high);
int min(int m, int n); // get the minimal value

int main(void)
{
    char    str[MAXLINE];
    int     ret;

    while (scanf("%s", str) != EOF) { // input in a loop
        ret = func(str, 0, strlen(str) - 1); // call func

        printf("%d\n", ret);
    }

    return 0;    
}

/* find the minimal number of characters in a string, 
 * which are needed removed to make the string be palindrome 
 */
int func(char *p, int low, int high)
{
    int     n;
    int     l;
    int     r;

    if (low >= high) {
        return 0;
    }

    if (p[low] == p[high]) { // needn't remove 
        n = func(p, low + 1, high - 1);          
    }
    else {
        l = func(p, low + 1, high);
        r = func(p, low, high - 1);
        n = min(l, r) + 1;
    } 

    return n;
}

/* return the minimal variable */
int min(int m, int n) 
{
    return (m < n ? m : n);

}

2 个答案:

答案 0 :(得分:1)

一个关键的改进是认识到当只消除字符串的一边时,另一边必须匹配(另一边有一个字符,即使它本身也是如此),否则为什么不消除双方?

当移除一侧的角色时,从另一侧向另一侧寻找另一侧的角色。 (总是找到匹配。)这消除了许多不必要的递归路径。

二次改进“短路”如下。无需测试其他组合,因为它们无法改善结果。

if (left == 1) return 1;

enter image description here

int func(const char *p, int low, int high) {
  int n;
  int left;
  int right;

  count++;
  if (low >= high) {
    return 0;
  }

  if (p[low] == p[high]) { // needn't remove
    n = func(p, low + 1, high - 1);
  } else {
#if 0
    left = func(p, low + 1, high);
    // if (left == 0) return 1;
    right = func(p, low, high - 1);
    n = min(left, right) + 1;
#else
    int delta;
    // remove low, keep high as part of palindrome
    delta = 1;
    while (p[low + delta] != p[high])
      delta++;
    left = func(p, low + delta, high) + delta;
    if (left == 1) return 1;

    // remove high, keep low as part of palindrome
    delta = 1;
    while (p[low] != p[high - delta])
      delta++;
    right = func(p, low, high - delta) + delta;
    if (right <= 2) return right;
    n = min(left, right);

    // remove first and last
    //int both = func(p, low + 1, high-1) + 1 + (high > (low + 1));
    int both = func(p, low + 1, high - 1) + 2;
    n = min(n, both);
#endif
  }
  return n;
}

鼠标悬停以获得OP's test string的最终结果(隐藏,以防OP不想立即看到它。)

  

计数= 13090 RET = 45 STR = 'jfdasflkjddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddfjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj'

小改进使用const。知道缓冲区不变,一些编译器将生成更有效的代码。更好的编译器可能会检测到这一点。

// int func(char *p, int low, int high)
int func(const char *p, int low, int high)

一些测试驱动程序代码

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

#define MAXLINE  4096
unsigned long long count = 0;

int func(const char *p, int low, int high);
int min(int m, int n); // get the minimal value

void testfunc(const char *str) {
  count = 0;
  int ret = func(str, 0, (int) strlen(str) - 1); // call func
  printf(" count = %llu", count);
  printf(" ret = %d", ret);
  printf(" str = '%s' ++", str);
  puts("");
  fflush(stdout);
}

int main(void) {
  char str[MAXLINE];
  int ret;

  char t[] =
      "jfdasflkjdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
          "ddddddddddddddfjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj";
  for (size_t i = 0; t[i]; i++) {
    strncpy(str, t, i);
    str[i] = 0;
    testfunc(str);
  }
  return 0;
}

int min(int m, int n) {
  return (m < n ? m : n);
}

int func(const char *p, int low, int high) {
  ...

答案 1 :(得分:0)

你不应该递归地调用它,因为这导致多次执行相同的检查(一些范围将被多次检查,而实际上只需要一次检查)。相反,你应该使用&#34;动态编程&#34;方法,从下到上构建。这意味着你需要创建一个二维数组windows_package,它存储范围dp[i][j], i<ji的最大回文长度。因此,如果j首先进行j=i+k的构建,那么k=0等等。