web-dev-qa-db-fra.com

Algorithme efficace pour compter le nombre de sous-chaînes divisibles par 3

Compte tenu d'une chaîne de chiffres décimaux, je dois trouver le nombre de toutes les sous-chaînes divisibles par 3 dans la plage L à R [à la fois inclus], où L & R sont index [basé] de la chaîne spécifiée.
[.____] string length <= 100000

J'ai essayé l'approche naïve d'itération de toutes les sous-chaînes possibles et de l'obtention de la réponse, mais ce n'est pas assez rapide, surtout pour plusieurs paires L & R.

Ensuite, j'ai essayé une approche DP. Je peux obtenir toutes les soustractions possibles divisibles par 3 dans l'ensemble de la chaîne, c'est-à-dire que je suis incapable de coder la solution DP qui donne un résultat pour une plage donnée.
Solution DP que j'ai essayé (pas complètement sûr de cela):

for(i=0 ; i<n ; i++) {
    for(j=0 ; j<3 ; j++) {
        dp[i][j]=0 ;
    }
    int curr = (input[i]-'0')%3 ;
    dp[i][curr]++ ;
    if(i) {
        for(j=0 ; j<3 ; j++) {
            if(curr % 3 == 0) { dp[i][j] += dp[i-1][j]; }
            if(curr % 3 == 1) { dp[i][j] += dp[i-1][(j+2)%3]; }
            if(curr % 3 == 2) { dp[i][j] += dp[i-1][(j+1)%3]; }
        }
    }
}
long long sum = 0;
for(i=0 ; i<n ; i++) { sum += dp[i][0] ; }

Cette solution peut-elle être modifiée pour donner des résultats pour une plage donnée [L, R]?

Après avoir cherché beaucoup, j'ai appris que les problèmes de requêtes de la gamme sont résolus par l'arbre de segment, mais je ne suis pas sûr de la manière dont l'arbre de segment + la propagation paresseux peut aider dans cette question.

Donc, quel est un moyen performant de compter toutes les sous-chaînes divisibles de 3 dans la gamme L à R [à la fois inclusives]?

Edit :
[.____] entrée: la première ligne contient la chaîne donnée. Les lignes suivantes contiennent deux entiers indiquant L et R respectivement, où L et R sont index (basés sur 1) de chaîne.

Saisir:
[.____] 301524 1 2 4 6 3 5

Sortir:
[.____] 3 1 1

Explication:
Quand L = 1 & R = 2, nous obtenons 3 substrings possibles, {3}, {0}, {30} & tous ceux-ci sont considérés comme un nombre décimal sont divisibles par 3. D'où la sortie.
[.____] Lorsque L = 4 & R = 6, nous obtenons 6 sous-chaînes possibles, {5} , {52}, {524}, {2}, {24}, {4} & sortir de ceux-ci seulement {24} est divisible par 3.

Les répétitions dans des sous-chaînes telles que 3333 comptent plusieurs fois, donc pour L = 1 à r = 4 La réponse serait 10, car nous avons quatre fois 3, trois fois 33, deux fois 333 et une fois 3333 (toutes divisibles par 3).

4
Aalok

Voici un Python solution au problème. Il est fondamentalement identique à la solution de @jasonn. Elle implémente également une fonction qui renvoie la valeur pour différentes paires de L et R. Il utilise moins mémoire lorsque vous faites les précalculements. Vous pouvez l'exécuter ici

#http://programmers.stackexchange.com/q/268022/51441


# s:
#   input digit string that should be checked
# L:
#   the start of the substring that should be investigated
# L:
#   the end of the substring that should be investigated
# cnt:
#   number of 3divisble substrings found so far
# ss0:
#   number of substrings that end at the current position
#   and are divisible by 3
# ss1:
#   number of substrings that end at the current position
#   and have the remainder 1 when divided by 3
# ss2:
#   number of substrings that end at the current position
#   and have the remainder 2 when divided by 3


def ss(s,L,R):
    cnt=0
    (ss0,ss1,ss2)=(0,0,0)
    for i in range(L,R+1):
        r=int(s[i])%3
        if r==0:
            ss0+=1
        Elif r==1:
            (ss0,ss1,ss2)=(ss2,ss0,ss1)
            ss1+=1
        Elif r==2:
            (ss0,ss1,ss2)=(ss1,ss2,ss0)
            ss2+=1
        cnt+=ss0
    return(cnt)

print(ss('392301',0,len('392301')-1))
print(ss('2035498',2,5))
1
miracle173