简体   繁体   English

如何有效地去除字符串中最少数量的字符以将其转换为回文

[英]how to efficiently get minimal number removals of characters of the string to convert it to palindrome

About the question how to convert a string to palindrome with minimum number of removals of characters of the string? 关于这个问题, 如何以最少的字符串去除次数将字符串转换为回文? . I write the program to test the answer accepted. 我编写程序来测试接受的答案。 But recursion takes too much time. 但是递归会花费太多时间。 How can this problem be solved or improved?Below is the answer accepted: 如何解决或改善此问题?下面是接受的答案:

Let dp[i, j] = minimum number of removals needed to convert the substring [i, j] to a palindrome. 令dp [i,j] =将子串[i,j]转换为回文所需要的最小移除次数。 We have: 我们有:

dp[i, i] = 0 for all i (every single character is a palindrome) To find dp[i, j], let's consider a random string. dp[i, i] = 0 for all i (every single character is a palindrome)为了找到dp [i,j],让我们考虑一个随机字符串。 We have two possibilities: 我们有两种可能性:

  1. The first and last characters are equal: a[i] == a[j]. 第一个和最后一个字符相等:a [i] == a [j]。 In this case, we can reduce the problem to finding the minimum number of characters that need to be deleted in order to make the substring [i+1, j-1] a palindrome. 在这种情况下,我们可以减少问题,找到为使子字符串[i + 1,j-1]成为回文式而需要删除的最少字符数。

  2. The first and last characters are not equal: a[i] != a[j]. 第一个和最后一个字符不相等:a [i]!= a [j]。 In this case, we need to remove one of them. 在这种情况下,我们需要删除其中之一。 We'll remove that which leads us to a better solution. 我们将删除导致我们找到更好解决方案的内容。

/* 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);

}

A key improvement is to recognize that when only one side of the string is eliminated, the other side must have a match (with a character on the other side, even if it is itself), else why not eliminate both sides? 一个重要的改进是认识到,当仅消除字符串的一侧时,另一侧必须具有匹配项(即使在字符串的另一侧也具有字符),否则为什么不消除两侧呢?

When a character from one side is removed, seek from that side toward the other for a match of the other side's character. 当从一侧的角色移出时,从另一侧向另一侧寻找与另一侧的角色匹配的角色。 (A match is always be found.) This eliminates many unnecessary recursion paths. (总是找到匹配项。)这消除了许多不必要的递归路径。

A secondary improvement "short-circuits" as below. 二次改进是“短路”,如下所示。 No need to test other combinations as they cannot improve the result. 无需测试其他组合,因为它们无法改善结果。

if (left == 1) return 1;

在此处输入图片说明

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;
}

Mouse over for final result of OP's test string (Hidden in case OP does not want to see it right away.) 将鼠标悬停在OP的测试字符串的最终结果上(如果OP不想立即看到它,则将其隐藏。)

count = 13090 ret = 45 str = 'jfdasflkjddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddfjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj' 计数= 13090 ret = 45 str ='jfdasflkjddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddfd

A minor improvement uses const . 较小的改进使用const Some compliers will generate more efficient code knowing the buffer is unchanging. 一些编译器会在知道缓冲区不变的情况下生成更有效的代码。 Better compilers may detect this anyways. 更好的编译器仍然可以检测到这一点。

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

Some test driver code 一些测试驱动程序代码

#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) {
  ...

you shouldn't call it recursively, because that leads to multiple execution of the same check (some range will be checked many times, when in reality only one check is required). 您不应递归调用它,因为这会导致多次执行同一检查(将多次检查某个范围,而实际上只需要执行一次检查)。 Instead u should use a "dynamic programming" method, build from bottom to top. 相反,您应该使用“动态编程”方法,从下到上进行构建。 What that means is u need to create a two-dimensional array, dp[i][j], i<j , which stores the length of a maximum palindrome in range i to j . 这意味着您需要创建一个二维数组dp[i][j], i<j ,该数组存储最大回文长度在ij范围内。 So if j=i+k first u proceed to build for k=0 , then for k=1 and so on. 因此,如果j=i+k首先,您继续针对k=0进行构建,然后针对k=1进行构建,依此类推。

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

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