如何使用递归计算字符串中的所有回文?

How to count all the palindromes in a string using recursion?

我有一个检查字符串是否为回文的递归函数,但我的作业要求我计算字符串中回文的数量(例如 kayak 有 2 个)。

我真的很困惑如何实现一个计算回文数的递归函数。这是我当前的代码:

function isPalindrome(string) {
  if (string.length <= 1) {
    return true;
  }

  let [ firstLetter ] = string;
  let lastLetter = string[string.length - 1];

  if (firstLetter === lastLetter) {
    let stringWithoutFirstAndLastLetters = string.substring(1, string.length - 1);
    return isPalindrome(stringWithoutFirstAndLastLetters);
  } else {
    return false;
  }
}

一种方法是首先 ,然后验证每个:

getAllSubstrings('kayak').filter(str => str.length >= 2 && isPalindrome(str))

function getAllSubstrings(str) {
  var i, j, result = [];

  for (i = 0; i < str.length; i++) {
      for (j = i + 1; j < str.length + 1; j++) {
          result.push(str.slice(i, j));
      }
  }
  return result;
}
function isPalindrome(string) {
  if (string.length <= 1) {
    return true;
  }

  let [ firstLetter ] = string;
  let lastLetter = string[string.length - 1];

  if (firstLetter === lastLetter) {
    let stringWithoutFirstAndLastLetters = string.substring(1, string.length - 1);
    return isPalindrome(stringWithoutFirstAndLastLetters);
  } else {
    return false;
  }
}
console.log(
  getAllSubstrings('kayak').filter(str => str.length >= 2 && isPalindrome(str))
);

我认为接受的答案实际上不起作用。它不会计算回文,除非它们位于字符串的中心,并且只要它们以相同的字母开头和结尾,就会计算不是回文的子字符串。 CertainPerformance 的答案可能会起作用,但我认为这会导致检查很多不需要检查的字符串。这是我想出的,我认为它适用于我添加的额外测试。

function countPalindromes(string) {
    if (string.length <= 1) {
    return 0;
    }

    count = 0

    for ( var i = 0; i < string.length; i++  ) {
    count += countPalindromesCenteredAt(string, i)
    count += countPalindromesCenteredAfter(string, i)
    }

    return count
}

function countPalindromesCenteredAt(string, i) {
    count = 0
    for ( var j = 1; i-j>=0 && i+j < string.length; j++  ) {
    if (string.charAt(i-j) === string.charAt(i+j)) {
        count += 1
    }
    else {
        return count
    }
    }

    return count
}

function countPalindromesCenteredAfter(string, i) {
    count = 0
    
    for ( var j = 1; i-j>=0 && i+j < string.length; j++  ) {
    if (string.charAt(i-j+1) === string.charAt(i+j)) {
        count += 1
    }
    else {
        return count
    }
    }

    return count
}

console.log(countPalindromes("kayak"));
console.log(countPalindromes("aya"));
console.log(countPalindromes("kayakcanoe"));
console.log(countPalindromes("kcanoek"));

当函数得到回文时很容易:

  1. 记录输入
  2. 没有边缘再试一次
  3. 输入不超过三个字符时停止
"kayak" -> "aya"

如果输入不是回文,请递归尝试“两端”,例如使用 "kayam" 尝试同时使用 "kaya""ayam" 并继续...

当字符串为 3 个(或更少)字符时,我们将停止递归。单个字符不是回文,检查两个或三个字符的字符串是否是回文是微不足道的。

                      kayam
                        |
                  +-------------+
                  |             |
                 kaya          ayam
                  |             |
              +-------+     +--------+
              |       |     |        |
             kay     aya   aya      yam

const reverse =
  ([...xs]) =>
    xs.reverse().join("");

const is_palindrome =
  a =>
      a.length === 1  ? false
    : a.length <= 3   ? a[0] === a[a.length-1]
                      : a === reverse(a);

const find_palindromes = str => {
  const scan =
    (x, xs = []) =>
        x.length <= 3     ? xs.concat(is_palindrome(x) ? x : [])
      : is_palindrome(x)  ? xs.concat
                              ( x
                              , scan(x.slice(1, -1))
                              )
                          : xs.concat
                              ( scan(x.slice(0, -1))
                              , scan(x.slice(1))
                              );

  return [...new Set(scan(str))];
};


console.log(find_palindromes("kayak").join());
console.log(find_palindromes("kayakkayak").join());
console.log(find_palindromes("kayakcanoe").join());
console.log(find_palindromes("kayam").join());
console.log(find_palindromes("appal").join());
console.log(find_palindromes("madamimadam").join());
console.log(find_palindromes("madamimadamkayak").join());

这是一个类似于 CertainPerformance 的答案,但对辅助函数使用递归:

const getSubstrings = (str) =>
  str .length == 0 
    ? []
    : [
        ... str .split ('') .map ((_, i) => str .slice (0, str .length - i)),
        ... getSubstrings (str .slice (1))
      ]

const isPalindrome = (str) =>
  str .length < 2
    ? true
    : str [0] === str .slice (-1) [0] && isPalindrome (str .slice (1, -1))

const getPalindromicSubstrings = (str) =>
  getSubstrings (str) 
    .filter (s => s.length > 1) 
    .filter (isPalindrome)

const countPalindromicSubstrings = (str) =>
  getPalindromicSubstrings (str) .length

const countUniquePalindromicSubstrings = (str) =>
  new Set(getPalindromicSubstrings (str)) .size

console .log (getPalindromicSubstrings ('madamimadam'))
console .log (countPalindromicSubstrings ('madamimadam'))
console .log (countUniquePalindromicSubstrings ('madamimadam'))
.as-console-wrapper {max-height: 100% !important; top: 0}

  • getSubstrings 正是您所期望的。 getSubstrings('abcd') returns ["abcd", "abc", "ab", "a", "bcd", "bc", "b", "cd", "c", "d"].

  • isPalindrome 表示空字符串和单字符字符串自动回文,对于另一个字符串,我们检查两个结束字符匹配,在剩余部分重复出现。

  • getPalindromicSubstrings 查找所有回文子串,跳过长度为 1.

    的子串
  • countPalindromicSubstrings returns 数一数。

  • countUniquePalindromicSubstrings 使用 Set 过滤掉重复项和 returns 计数。

如果需要,我们也可以用类似的方式轻松编写 getUniquePalindromicSubstrings

getSubstrings 是唯一具有任何复杂性的函数。它通过重复将我们的字符串切片为从 length1 的值,然后在以第二个字符开始的字符串上重复出现,当我们的输入为空时停止。