web-dev-qa-db-fra.com

Comptage des sous-chaînes palindromiques dans O(n)

Étant donné une chaîne (en supposant uniquement des caractères anglais) S de longueur n, nous pouvons compter le nombre de sous-chaînes palindromiques avec l'algorithme suivant:

for i = 0 to |S| do
    p1 = number of palindromes centered in i (odd length)
    p2 = number of palindromes centered in i and i+1 (even length)

    add p1 + p2 to total number of palindromic substrings of S

Le code ci-dessus est cependant O(n^2).

Je suis intéressé par un algorithme qui résout ce problème dans O(n). Je sais avec certitude qu'il en existe un, car j'ai entendu plusieurs personnes le dire, et le problème existe sur un site de juge en ligne local avec une limite supérieure de 1 000 000 Sur n, mais je 'ai jamais vu l'algorithme et ne semble pas être en mesure de le trouver.

Mise à jour:

L'idée générale que j'ai est de calculer len[i] = length of the longest palindrome centered at the character 2i + 1 Et un tableau similaire pour les palindromes de même longueur. Avec une bonne comptabilité, il devrait être possible de calculer cela dans O(1) pour chaque caractère, ce qui nous permettra de compter un grand nombre de palindromes à la fois. Cependant, je ne sais pas comment calculer exactement cela.

J'accepterai une solution qui utilise O(n) et peut-être même O(n log n) de la mémoire supplémentaire. Je pense que c'est impossible sans cela.

Toutes les bonnes idées ou références sont appréciées.

31
IVlad

Le site suivant montre un algorithme pour calculer la plus longue sous-chaîne palindromique en O(n) temps, et le fait en calculant la plus longue sous-chaîne palindromique à chaque centre possible, puis en prenant le maximum. Donc, vous devrait pouvoir le modifier facilement selon vos besoins.

http://www.akalin.cx/2007/11/28/finding-the-longest-palindromic-substring-in-linear-time/

EDIT: Le premier lien semble un peu fragile à y regarder de plus près, alors en voici un autre:

http://zhuhcheng.spaces.live.com/Blog/cns!DE38E96268C49F28!311.entry?wa=wsignin1.0&sa=707413829

7
Paul Accisano

Pour les chaînes "normales", il devrait être plutôt efficace de regarder chaque caractère comme le "centre" potentiel d'un palindrome et de vérifier ensuite si les personnages environnants en construisent réellement un:

# check odd palindromes
for center in range(len(ls)):
   # check how many characters to the left and right of |center|
   # build a palindrome
   maxoffs = min(center, len(ls)-center-1)
   offs = 0
   while offs <= maxoffs and ls[center-offs] == ls[center+offs]:
      offs += 1
   offs -= 1
   print ls[center-offs : center+offs+1]                                    

# check for even palindromes
for center in range(len(ls)-1):
   maxoffs = min(center, len(ls)-center-2)
   offs = 0
   while offs <= maxoffs and ls[center-offs] == ls[center+offs+1]:
      offs += 1
   offs -= 1
   if offs >= 0:
      print ls[center-offs : center+offs+2]

Pour les chaînes normales, cela devrait être à peu près O (n), bien que dans le pire des cas, par exemple si la chaîne se compose d'un seul caractère répété encore et encore, elle prendra toujours O (n2) temps.

1
sth

Considérez une chaîne S="aaabb".

Ajouter un caractère '$' aux deux extrémités de la chaîne et entre tous les deux caractères consécutifs pour changer la chaîne en S="$a$a$a$b$b$" et appliquez algorithme de Manacher pour cette chaîne S.

La nouvelle chaîne S est de longueur 2n + 1 ce qui nous donne un temps d'exécution de O (2n + 1) qui est identique à O (n).

index :  1 2 3 4 5 6 7 8 9 10 11
A     :  1 3 5 7 5 3 1 3 5  3  1
S     :  $ a $ a $ a $ b $  b  $

Array A est le résultat de l'algorithme de Manacher.

Maintenant, la somme de A[i]/4 pour l'index où '$', autre (A[i]+1)/4 pour chaque autre caractère de 1 <= i <= n est votre réponse.

Ici, $ agit comme un centre pour les sous-chaînes palidromiques de longueur paire et la longueur impaire peut être calculée normalement. La réponse à ce cas est:

0 + 1 + 1 + 2 + 1 + 1 + 0 + 1 + 1 + 1 + 0 = 9 (a, a, aaa, a, b, b, aa, aa, bb).

1
Aakash Prakash