Сколько палиндромов может быть образовано выбором символов из строки? - PullRequest
23 голосов
/ 09 января 2010

Я публикую это от имени друга, так как считаю, что это довольно интересно:

Возьми строку "abb".Оставляя любое количество букв меньше длины строки, мы получаем 7 строк.

abb ab ab bb abb

Из этих 4 - палиндромы.

Аналогично для строки

"hihellolookhavealookatthispalindromexxqwertyuiopasdfghjklzxcvbnmmnbvcxzlkjhgfdsapoiuytrewqxxsoundsfamiliardoesit"

(длина 112 - 112 *) * 112 * может * 112 * 2 * 112 * 2?

Ниже приведена его реализация (хотя в 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;
}

Ответы [ 9 ]

15 голосов
/ 09 января 2010

Прежде всего, решение вашего друга, похоже, содержит ошибку, так как 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, чтобы найти число возможностей для внутреннего палиндрома.

EDIT

  • Уточнение: когда я говорю «количество палиндромов, содержащихся в строке», это включает в себя несмежные подстроки. Например, палиндром «aba» содержится в «abca».

  • Можно уменьшить использование памяти до O (n), воспользовавшись тем, что для вычисления numFound[startPos][x] требуется только знание numFound[startPos+1][y] для всех y. Я не буду этого делать, поскольку это немного усложняет код.

  • Предварительно созданные списки индексов, содержащие каждую букву, могут ускорить внутренний цикл, но в целом он все равно будет O (n ^ 3).

6 голосов
/ 11 февраля 2011

У меня есть способ сделать это за O (N ^ 2) времени и O (1) пространства, однако я думаю, что должны быть и другие лучшие способы.

основная идея заключалась в том, что длинный палиндром должен содержать маленькие палиндромы, поэтому мы ищем только минимальное совпадение, что означает два вида ситуаций: «аа», «аба». Если мы нашли что-либо из этого, развернитесь, чтобы увидеть, является ли это частью длинного палиндрома.

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

14 июня 2012 г. После некоторого расследования я считаю, что это лучший способ сделать это. быстрее принятого ответа.

2 голосов
/ 09 января 2010

Мое решение использует 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

РЕДАКТИРОВАТЬ: Я неверно истолковал проблему как смежные версии проблемы подстроки. Теперь, учитывая, что кто-то хочет найти число палиндромов для несмежной версии, я сильно подозреваю, что можно просто использовать математическое уравнение для его решения, учитывая количество различных символов и их соответствующее количество символов.

2 голосов
/ 09 января 2010

Есть ли пробег в начальном обходе и построении индекса всех вхождений каждого символа.

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

Теперь работает слева, h, только возможные палидромы находятся в 3 и 17, есть ли у char [0 + 1] == char [3 -1] и т. Д. Палиндром. действительно ли char [0 + 1] == char [27 -1] нет, дальнейший анализ char [0] не требуется.

Переходите к символу [1], нужно только пример char [30 -1] и внутрь.

Тогда, возможно, вы станете умнее, когда вы определили палиндром, бегущий из положения x-> y, все внутренние подмножества являются известными палиндромами, поэтому мы рассмотрели некоторые элементы, которые могут исключить эти случаи из более позднего изучения.

1 голос
/ 10 марта 2010

Вот программа для поиска всех возможных палиндромов в строке, написанной на Java и C ++.

1 голос
/ 09 января 2010

Хммм, думаю, я бы посчитал вот так:

Каждый персонаж сам по себе является палиндромом (за исключением повторных символов).
Каждая пара одного и того же символа.
Каждая пара одного и того же персонажа со всеми палиндромами, зажатыми посередине, которая может быть сделана из струны между повторениями.
Применить рекурсивно.

Это похоже на то, что вы делаете, хотя я не уверен, что вы не учитываете двойные случаи с повторяющимися символами.

Так что, в принципе, я не могу придумать лучшего способа.

EDIT:
Думая еще немного, Это можно улучшить с помощью кэширования, потому что вы иногда считаете палиндромы в одной и той же подстроке более одного раза. Итак, я полагаю, это демонстрирует, что определенно есть лучший способ.

0 голосов
/ 27 сентября 2013
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;
}
0 голосов
/ 09 августа 2013
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;

}
0 голосов
/ 09 января 2010

Я не уверен, но вы можете попробовать немного Фурье. Эта проблема напомнила мне об этом: Алгоритм O (nlogn) - Найти три равномерно расположенных в двоичной строке

Только мои 2цента

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...