web-dev-qa-db-fra.com

Nombre de chaînes contenant une sous-chaîne spécifique

J'ai vu de nombreuses questions (et réponses) concernant le nombre de chaînes binaires (E.g "10010" contenant des sous-chaînes binaires (E.g "00"). J'aimerais savoir s'il y a un moyen de généraliser ceci:

Donné une longueur n et une chaîne s (peut contenir des lettres A...Z), combien différent chaînes de longueur n, qui contiennent la sous-chaîne s au moins une fois, existe-t-il?

Ces types de questions sont souvent résolvables avec la combinatoire, mais j'aimerais trouver une solution de programmation dynamique (et j'ai donc posté cette question ici au lieu de MathExchange).

Personnellement, je cours sur la vapeur froide ici. Bien sûr, j'ai essayé certaines méthodes combinatoires:

n = 5
s = CAR
? = unknown character

all possibilities:

CAR??
?CAR?
??CAR

Cela se résume essentiellement à 26^0*26^2 + 26^1*26^1 + 26^2*26^0 = 3 * 26^2 = 2028 Possibilités que je suppose est correcte. Considérez cependant le cas suivant:

n = 7
s = CAR
? = unknown character

all possibilities:

CAR????
?CAR???
??CAR??
???CAR?
????CAR

Quel est le problème? Eh bien, il y a 3 postes pouvant produire des résultats en double:

CARCAR?
CAR?CAR
?CARCAR

Maintenant, le nombre de chaînes possibles est

(2 * 26^7) + (2 * 26^1 * 26^6) + (2 * 26^2 * 26^5) + (2 * 26^3 * 26^4) - (3 * 26)

Je ne peux pas généraliser ça.

Ai-je attrapé l'intérêt de quelqu'un? Toute aide est appréciée.

6
Meri Craig

Reconnaissement Solution de Hirle , j'ai toujours pensé que je posterais le mien.

Je me suis approché de cela d'une petite direction différente. Après avoir pensé, le programme le plus simple de tous a tenu la clé:
Automaton fini déterministe. Cela pourrait être résolu avec une DFA qui recherchait des occurrences d'une chaîne, comme avec elle, je peux calculer une matrice d'adjacence que je peux utiliser ultérieurement. J'ai décidé de sauter les trivalities et je viens de copier un code de côté du net pour la version déterministe de Knuth-Morris-Pratt Algorithm (l'original si non négliniste, qui ne sert pas nos objectifs).

Le code ci-dessous provient de ici , avec de petites modifications:

public static int[][] dfa;

// create the DFA from a String
public static void kmp(String pat) {

    // build DFA from pattern
    int m = pat.length();
    dfa = new int[26][m]; 
    dfa[pat.charAt(0) - 'A'][0] = 1; 
    for (int X = 0, j = 1; j < m; j++) {
        for (int c = 0; c < 26; c++) 
            dfa[c][j] = dfa[c][X];          // Copy mismatch cases. 

        dfa[pat.charAt(j) - 'A'][j] = j+1;  // Set match case. 
        X = dfa[pat.charAt(j) - 'A'][X];    // Update restart state. 
    } 
}

Pour le motif ABC Cela génère la matrice 2D-matrice suivante, ou DFA, si vous voulez:

[1, 1, 1]
[0, 2, 0]
[0, 0, 3]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]

Ici, par exemple, la première ligne signifie que si le caractère vu est "A", alors en fonction de notre état Nous allons à l'état indiqué par la ligne.

Disons que l'état que nous sommes actuellement est l'état initial (état 0). Si nous voyons maintenant le personnage 'A', l'état suivant nous serait dit par le premier élément de la première rangée, qui est 1. Alors maintenant, notre État est 1. Disons que nous avons encore vu le personnage "A". Notre état resterait 1, car le deuxième élément de la première rangée est également 1. D'accord, dites maintenant que nous avons vu le caractère 'B'. Notre État passera à 2, puisque dans le deuxième élément de B-Row (le deuxième rangée) est 2.

En substance, notre État n'aurait que 3 seulement si nous avons vu les personnages ABC successivement. Les rangées D-Z sont toutes des zéros, car si nous avons déjà vu un caractère autre que A-C, nous devons commencer la recherche depuis le début (qui est une, dans ce cas).

Pour généraliser, l'élément matriciel [0][1] nous dit l'état que nous devrions aller à, si le personnage que nous avons vu était A et nous étions dans l'état 1. De même, l'élément matriciel [6][6] nous a dit notre prochain état si le personnage que nous avons vu était G, et nous étions dans l'état 6 (dans la matrice ci-dessus, nous n'avons bien sûr pas l'état 6, puisque nous l'avons construite avec une chaîne de longueur 3).

Quoi qu'il en soit, nous avons maintenant tout ce dont nous avons besoin pour construire la matrice de l'adjacence. Sans autre ADO, voici mon code pour cela:

String s = "ABC"; // The string we built our dfa with, e.g the string we are "searching"

paths = new long[s.length() + 1][s.length() + 1];
for (int k = 0; k < 26; k++)
    for (int i = 0; i < dfa[0].length; i++)
        paths[i][dfa[k][i]]++;

paths[paths.length - 1][paths.length - 1] = 26;

Donc, si nous construisions la matrice d'adjacence d'une DFA construite avec la chaîne "ABC", la matrice de l'adjacence ressemblerait à ceci:

[25, 1, 0, 0]
[24, 1, 1, 0]
[24, 1, 0, 1]
[0 , 0, 0, 26]

Ici paths[i][j] nous dirait combien de façons qu'il doit accéder à l'état j de l'état i. Par exemple, il y a 25 façons d'arriver à l'état 0 de l'état 0, car il n'ya qu'un moyen d'accéder à un autre état de l'état 0. Aussi, il y a 26 façons de résulter au dernier état, si nous sommes dans le dernier état.

Si nous avions tout construit jusqu'à présent avec la chaîne "AA", la matrice de l'adjacence ressemblerait à ceci:

[25, 1, 0]
[25, 0, 1]
[0, 0, 26]

Alors, qu'est-ce que maintenant? Nous avons notre matrice d'adjacence OH-SO-puissante qui nous raconte des choses cool, que faire avec ça? La théorie du graphique entre en jeu.

Référencement Wolfram Alpha :

l'élément (u,v)th de la puissance kth de la matrice d'adjacence de G donne le nombre de chemins de longueur k entre les sommets u et v.

Donc, ce que c'est dire, c'est que si nous élevons notre matrice d'adjacence dans, disons, la 3ème puissance, alors l'élément paths[0][k] nous dit combien de chemins de longueur 3 il y a de l'état 0 à l'état k.

Je vais préparer un peu plus pour cela: si nous avons une chaîne S, et avec cette chaîne, nous pouvons entraîner le dernier état de la DFA, puis nous considérons S à être [~ # ~] OK [~ # ~] . Donc, ce que nous voulons savoir pour obtenir la réponse au problème d'origine, c'est combien de cordes ok il y a dans de plus grandes chaînes de longueur n.

C'est pourquoi nous élevons la matrice d'adjacence dans la puissance nth. Maintenant l'élément paths[0][k], où k est la longueur de la chaîne que nous recherchons, devrait nous dire combien de cordes ok il y a dans une plus grande chaîne de longueur n, ce qui vient d'être la réponse. à notre problème.

Mon code pour soulever une matrice en nth puissance:

public static long[][] matrixToPower(long[][] a, int p) {
    long[][] b = a;
    for (int n = 1; n < p; n++)
        a = matrixMultiplication(a, b);
    return a;
}

public static long[][] matrixMultiplication(long[][] m1, long[][] m2) {
    int len = m1.length;
    long[][] mResult = new long[len][len];
    for(int i = 0; i < len; i++) {
        for(int j = 0; j < len; j++) {
            for(int k = 0; k < len; k++) {
                mResult[i][j] += (m1[i][k] * m2[k][j]) % M;
                mResult[i][j] %= M;
            }
        }
    }
    return mResult;
}

Je prends un modulo de taille 10^9 + 7 pour obtenir des réponses fiables pour une grande entrée.

Code complet:

import Java.util.*;

public class Program {

    public static final int M = 1000000007;

    public static int[][] dfa;
    public static long[][] paths;

    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        String s = sc.next();

        kmp(s); // generate dfa

        paths = new long[s.length() + 1][s.length() + 1];
        for (int k = 0; k < 26; k++)
            for (int i = 0; i < dfa[0].length; i++)
                paths[i][dfa[k][i]]++;            
        paths[paths.length - 1][paths.length - 1] = 26;

        paths = matrixToPower(paths, n);

        System.out.println(paths[0][s.length()]);
    }

    public static long[][] matrixToPower(long[][] a, int p) {
        long[][] b = a;
        for (int n = 1; n < p; n++)
            a = matrixMultiplication(a, b);
        return a;
    }

    public static long[][] matrixMultiplication(long[][] m1, long[][] m2) {
        int len = m1.length;
        long[][] mResult = new long[len][len];
        for(int i = 0; i < len; i++) {
            for(int j = 0; j < len; j++) {
                for(int k = 0; k < len; k++) {
                    mResult[i][j] += (m1[i][k] * m2[k][j]) % M;
                    mResult[i][j] %= M;
                }
            }
        }
        return mResult;
    }

    // create the DFA from a String
    public static void kmp(String pat) {
        // build DFA from pattern
        int m = pat.length();
        dfa = new int[26][m]; 
        dfa[pat.charAt(0) - 'A'][0] = 1; 
        for (int X = 0, j = 1; j < m; j++) {
            for (int c = 0; c < 26; c++) 
                dfa[c][j] = dfa[c][X];         // Copy mismatch cases. 
            dfa[pat.charAt(j) - 'A'][j] = j+1; // Set match case. 
            X = dfa[pat.charAt(j) - 'A'][X];   // Update restart state. 
        } 
    }

}
2
Olavi Mustanoja

J'ai une idée de résoudre le problème comme un problème de programmation dynamique.

Voyons un problème symétrique: combien de chaînes ne contient pas de sous-chaîne spécifique t?

Supposons que la longueur de la chaîne requise est N, et l'une d'une chaîne valide est S. (n == s.length ())

Nous définissons un tableau 2D DP [N] [T.Length ()],
[.____] Utiliser DP [I, J] pour représenter "Combien de combinaisons de biens que nous avons, lorsque le suffixe de la sous-chaîne S [0 ... I] est la même que le préfixe de T, dans lequel le plus longtemps Longueur (qui est variable 'J') du suffixe/préfixe? ".

dans un autre mot : pour la constante i, découvrez max {len} dans "S [i-len + 1, i] == t [0 ... len]", et dp [i , Len] signifie le nombre de combinaison de cet état.

Par exemple :
[.____] T = abcabc et s [0 ... I]:
[.____] J = 6 signifie que "les 6 caractères à la fin de S" est abcabc. (S[i-5...I] == abcabc)
[.____] J = 3 signifie que "les 6 caractères à la fin de S" est ### ABC, et ###!="abc".
Alors dp [i, j = 3] ne contient pas dp [i, j = 6]

et nous avons le code ici

Pour les performances, nous utilisons un algorithme KMP pour préproduire la chaîne T et générer un tableau 'NXT'.

T  = '*'+T; // to avoid -1 in index of array dp. (dp[i,-1])
geneNext();
dp[0][0]=25; dp[0][1]=1;
for (int i=0; i<n; i++) {
    for (int j=0; j<T.size()-1; j++) {
        //cout<<"dp["<<i<<","<<j<<"] = "<<dp[i][j]<<endl;
        for (int ch=0; ch<26; ch++) {//here we assume S[i+1]=ch.
            int tmp;// tmp is the new variable 'j' for S[0...i+1].
            for (tmp=j+1; tmp>0; tmp = nxt[tmp])
                if (T[tmp] == ch+'a')
                    break;
            dp[i+1][tmp] = dp[i+1][tmp]+dp[i][j];
        }
    }
}
int ans = 0;
for (int i=0; i<T.size()-1; i++)
    ans = ans + dp[n-1][i];
cout<<ans<<endl; // ans is my symmetric problem.
cout<<26^n - ans<<endl; // the answer for poster's problem.
0
vrqq