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

筅森魡賤 提交于 2020-12-11 06:01:22

问题


I have a recursive function that checks if a string is a palindrome, but my assignment asks me to count the number of palindromes in a string (for example kayak has 2).

I'm really confused about how I can implement a recursive function that counts the number of palindromes. Here's my current code:

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

回答1:


I think the accepted answer does not actually work. It will not count palindromes unless they are centered in the string and will count substrings that are not palindromes if as long as they start and end with the same letter. The answer from CertainPerformance would probably work but I think it would result in checking a lot of strings that don't need to be checked. Here's what I came up with, I think it works for the extra tests I've added.

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"));



回答2:


When the function gets a palindrome it is easy:

  1. Record the input
  2. Try again without the edges
  3. Stop when input is three characters or less
"kayak" -> "aya"

If the input isn't a palindrome try "both ends" recursively e.g. with "kayam" try with both "kaya" and "ayam" and keep going...

We stop the recursion when a string is 3 (or less) characters. A single character is not a palindrome and checking whether a two or three characters string is a palindrome is trivial.

                      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());



回答3:


One method would be to first get all substrings, then validate each:

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



回答4:


If you only want a single function, you could add a second argument (called count or such) and call it with countPalindromes("kayak", 0) (I changed your function's name but the rest should almost stay intact).

Then, you change the return statements to return count instead, and in the firstLetter === lastLetter case, you return countPalindromes(stringWithoutFirstAndLastLetters, count + 1).

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

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

  if (firstLetter === lastLetter) {
    let stringWithoutFirstAndLastLetters = string.substring(1, string.length - 1);
    return countPalindromes(stringWithoutFirstAndLastLetters, count + 1);
  } else {
    // edited to return 0, was "count"
    return 0;
  }
}

console.log(countPalindromes("kayak", 0));
console.log(countPalindromes("aya", 0));
console.log(countPalindromes("kcanoek", 0));



回答5:


Here's an answer similar to that from CertainPerformance, but using recursion for the helper functions:

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 does just what you'd expect. getSubstrings('abcd') returns ["abcd", "abc", "ab", "a", "bcd", "bc", "b", "cd", "c", "d"].

  • isPalindrome says that the empty string and single-character strings are automatically palindromes and that for another string we check that the two end characters match, recurring on the remainder.

  • getPalindromicSubstrings finds all the substrings that are palindromes, skipping those of length 1.

  • countPalindromicSubstrings returns a count of those.

  • countUniquePalindromicSubstrings uses a Set to filter out duplicates and returns that count.

We could also easily write a getUniquePalindromicSubstrings in a similar manner if needed.

getSubstrings is the only function with any complexity. It operates by repeatedly slicing our string from to a value varying from length down to 1, then recurring on the string starting with the second character, stopping when our input is empty.



来源:https://stackoverflow.com/questions/64596115/how-to-count-all-the-palindromes-in-a-string-using-recursion

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!