繁体   English   中英

通过从字符串中选择字符可以形成多少个回文?

[英]How many palindromes can be formed by selections of characters from a string?

我代表一位朋友发帖,因为我觉得这很有趣:

取字符串“abb”。 通过遗漏少于字符串长度的任意数量的字母,我们最终得到7个字符串。

abb ab ab bb abb

其中4个是回文。

同样对于字符串

“hihellolookhavealookatthispalindromexxqwertyuiopasdfghjklzxcvbnmmnbvcxzlkjhgfdsapoiuytrewqxxsoundsfamiliardoesit”

(长度为112弦)2 ^ 112 - 可以形成1个弦。

其中有多少是回文?

下面是他的实现(在C ++中,C也很好)。 用很长的词来说它很慢; 他想知道什么是最快的算法(我也很好奇:D)。

#include <iostream>
#include <cstring>

using namespace std;



void find_palindrome(const char* str, const char* max, long& count)
{
    for(const char* begin = str; begin < max; begin++) {
        count++;
        const char* end = strchr(begin + 1, *begin);
        while(end != NULL) {
            count++;
            find_palindrome(begin + 1, end, count);
            end = strchr(end + 1, *begin);
        }
    }
}


int main(int argc, char *argv[])
{
    const char* s = "hihellolookhavealookatthis";
    long count = 0;

    find_palindrome(s, strlen(s) + s, count);

    cout << count << endl;
}

首先,你的朋友的解决方案似乎有一个bug,因为strchr可以搜索过去的max 即使你解决了这个问题,解决方案也是指数级的。

为了更快的解决方案,您可以使用动态编程在O(n ^ 3)时间内解决此问题。 这将需要O(n ^ 2)额外的内存。 请注意,对于长字符串,即使是我在此处使用的64位整数也不足以容纳解决方案。

#define MAX_SIZE 1000
long long numFound[MAX_SIZE][MAX_SIZE]; //intermediate results, indexed by [startPosition][endPosition]

long long countPalindromes(const char *str) {
    int len = strlen(str);
    for (int startPos=0; startPos<=len; startPos++)
        for (int endPos=0; endPos<=len; endPos++)
            numFound[startPos][endPos] = 0;

    for (int spanSize=1; spanSize<=len; spanSize++) {
        for (int startPos=0; startPos<=len-spanSize; startPos++) {
            int endPos = startPos + spanSize;
            long long count = numFound[startPos+1][endPos];   //if str[startPos] is not in the palindrome, this will be the count
            char ch = str[startPos];

            //if str[startPos] is in the palindrome, choose a matching character for the palindrome end
            for (int searchPos=startPos; searchPos<endPos; searchPos++) {
                if (str[searchPos] == ch)
                    count += 1 + numFound[startPos+1][searchPos];
            }

            numFound[startPos][endPos] = count;
        }
    }
    return numFound[0][len];
}

说明:

数组numFound[startPos][endPos]将保存子字符串中包含的回文数,其索引为startPos到endPos。

我们遍历所有索引对(startPos,endPos),从短跨度开始到移动到较长跨度。 对于每个这样的对,有两种选择:

  1. str[startPos]的字符不在回文中。 在这种情况下,有numFound[startPos+1][endPos]可能的回文 - 我们已经计算过的数字。

  2. str[startPos]中的字符位于回文结构中(在其开头)。 我们扫描字符串以找到匹配的字符放在回文末尾。 对于每个这样的字符,我们使用numFound已经计算的结果来查找内部回文的可能性数量。

编辑

  • 澄清:当我说“字符串中包含的回文数”时,这包括非连续的子串。 例如,回文“aba”包含在“abca”中。

  • 通过利用numFound[startPos][x]仅需要知道所有y的numFound[startPos+1][y]这一事实,可以将内存使用减少到O(n)。 我不会在这里这样做,因为它使代码复杂化了一些。

  • 预生成包含每个字母的索引列表可以使内循环更快,但总体上仍然是O(n ^ 3)。

我有办法在O(N ^ 2)时间和O(1)空间中做到这一点,但我认为必须有其他更好的方法。

基本的想法是长回文必须包含小回文,所以我们只搜索最小匹配,这意味着两种情况:“aa”,“aba”。 如果我们找到了,那么扩展以查看它是否是长回文的一部分。

    int count_palindromic_slices(const string &S) {
        int count = 0;

        for (int position=0; position<S.length(); position++) {
            int offset = 0;

            // Check the "aa" situation
            while((position-offset>=0) && (position+offset+1)<S.length() && (S.at(position-offset))==(S.at(position+offset+1))) {
                count ++;
                offset ++;
            }

            offset = 1;  // reset it for the odd length checking
            // Check the string for "aba" situation
            while((position-offset>=0) && position+offset<S.length() && (S.at(position-offset))==(S.at(position+offset))) {
                count ++;
                offset ++;
            }
        }
        return count;
    }

2012年6月14日经过一番调查,我相信这是最好的方法。 比接受的答案快。

在进行初始遍历并建立每个角色的所有出现的索引时,是否有任何里程数。

 h = { 0, 2, 27}
 i = { 1, 30 }
 etc.

现在从左边开始,h,只有可能的palidromes在3和17,char [0 + 1] == char [3 -1]等等得到了回文。 char [0 + 1] == char [27 -1]否,不需要进一步分析char [0]。

转到char [1],只需要示例char [30 -1]和向内。

然后可能变得聪明,当你确定从x-> y位置运行的回文时,所有内部子集都是已知的回文,因此我们已经处理了一些项目,可以在以后的检查中消除这些情况。

我的解决方案使用O(n)内存和O(n^2)时间,其中n是字符串长度:

palindrome.c:

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

typedef unsigned long long ull;

ull countPalindromesHelper (const char* str, const size_t len, const size_t begin, const size_t end, const ull count) {
  if (begin <= 0 || end >= len) {
    return count;
  }
  const char pred = str [begin - 1];
  const char succ = str [end];
  if (pred == succ) {
    const ull newCount = count == 0 ? 1 : count * 2;
    return countPalindromesHelper (str, len, begin - 1, end + 1, newCount);
  }
  return count;
}

ull countPalindromes (const char* str) {
  ull count = 0;
  size_t len = strlen (str);
  size_t i;
  for (i = 0; i < len; ++i) {
    count += countPalindromesHelper (str, len, i, i, 0);  // even length palindromes
    count += countPalindromesHelper (str, len, i, i + 1, 1); // odd length palindromes
  }
  return count;
}

int main (int argc, char* argv[]) {
 if (argc < 2) {
  return 0;
 }
 const char* str = argv [1];
 ull count = countPalindromes (str);
 printf ("%llu\n", count);
 return 0;
}

用法:

$ gcc palindrome.c -o palindrome
$ ./palindrome myteststring

编辑:我误解了问题作为问题的连续子串版本。 现在假设有人想要找到非连续版本的回文数,我强烈怀疑,只要使用不同的字符数及其各自的字符数,就可以使用数学方程来解决它。

嗯,我想我会这样算:

每个角色都是它自己的回文(减去重复的字符)。
每对相同的角色。
每对相同的角色,所有的回文夹子都夹在中间,可以用重复之间的串子制成。
递归申请。

这似乎是你在做什么,虽然我不确定你不会重复计算重复字符的边缘情况。

所以,基本上,我想不出更好的方法。

编辑:
再考虑一下,它可以通过缓存来改进,因为有时你会在同一个子串中多次计算回文。 所以,我想这表明肯定有更好的方法。

这是一个程序,用于查找用Java和C ++编写的字符串中的所有可能的回文

int main()
 {
    string palindrome;

    cout << "Enter a String to check if it is a Palindrome";

    cin >> palindrome;

    int length = palindrome.length();

    cout << "the length of the string is " << length << endl;

    int end = length - 1;
    int start = 0;
    int check=1;

    while (end >= start) {
        if (palindrome[start] != palindrome[end]) {
            cout << "The string is not a palindrome";
            check=0;
            break;
        }
        else
        {
            start++;
            end--;

        }

    }
    if(check)
    cout << "The string is a Palindrome" << endl;

}
public String[] findPalindromes(String source) {

    Set<String> palindromes = new HashSet<String>();        
    int count = 0;
    for(int i=0; i<source.length()-1; i++) {            
        for(int j= i+1; j<source.length(); j++) {               
            String palindromeCandidate = new String(source.substring(i, j+1));
            if(isPalindrome(palindromeCandidate)) {
                palindromes.add(palindromeCandidate);                   
            }
        }
    }

    return palindromes.toArray(new String[palindromes.size()]);     
}

private boolean isPalindrome(String source) {

    int i =0;
    int k = source.length()-1;      
    for(i=0; i<source.length()/2; i++) {            
        if(source.charAt(i) != source.charAt(k)) {
            return false;
        }
        k--;
    }       
    return true;
}

我不确定,但你可能会尝试使用傅立叶。 这个问题提醒我: O(nlogn)算法 - 在二进制字符串中找到三个均匀间隔的算法

只是我的2cents

暂无
暂无

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

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