Подсчитайте подпоследовательности длины 4, кратные 9

Для подсчета подпоследовательностей длины 4 строки длины n, которые делятся на 9.

Например, если входная строка - 9999, тогда cnt = 1

Мой подход похож на метод грубой силы и требует O (n ^ 3). Есть ли лучший подход, чем этот?


person Luv    schedule 07.08.2012    source источник
comment
Если входная строка - 99999 (5 цифр), что будет подсчитывать? Учитываются ли только уникальные подпоследовательности или каждый дубликат учитывается отдельно?   -  person Steve314    schedule 08.08.2012
comment
@ Steve314 Каждый дубликат подсчитывается отдельно   -  person Luv    schedule 08.08.2012


Ответы (4)


Если вы хотите проверить, делится ли число на 9, вам лучше посмотреть здесь.

Кратко опишу метод:

checkDividedByNine(String pNum) :
If pNum.length < 1
   return false
If pNum.length == 1
   return toInt(pNum) == 9;
Sum = 0
For c in pNum:
    Sum += toInt(pNum)
return checkDividedByNine(toString(Sum))

Таким образом, вы можете сократить время работы до менее O (n ^ 3).

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

РЕДАКТИРОВАТЬ 2: лучший подход: вы можете использовать динамическое программирование:

Для строки S длиной N:

D [i, j, k] = количество подпоследовательностей длины j в строке S [i..N], значение которых по модулю 9 == k.

Где 0 ‹= k‹ = 8, 1 ‹= j‹ = 4, 1 ‹= i‹ = N.

D[i,1,k] = simply count the number of elements in S[i..N] that = k(mod 9).
D[N,j,k] = if j==1 and (S[N] modulo 9) == k, return 1. Otherwise, 0.
D[i,j,k] = max{ D[i+1,j,k], D[i+1,j-1, (k-S[i]+9) modulo 9]}.

И вы вернете D [1,4,0].

Получается стол размером - N х 9 х 4.

Таким образом, общее время выполнения, если вычисление по модулю занимает O (1), составляет O (n).

person barak1412    schedule 07.08.2012
comment
Что я знаю. Я хочу уменьшить сложность проблемы. - person Luv; 08.08.2012
comment
Если pNum.length ‹1 вернуть false; Почему? Почему бы не вернуть 0? - person Vikram; 08.08.2012
comment
checkDividedByNine возвращает логическое значение, а не число. - person barak1412; 08.08.2012
comment
@Luv посмотри на мое решение для динамического программирования - person barak1412; 08.08.2012
comment
@ barak1412 См. мой рабочий код для указанной выше проблемы с использованием DP - person Luv; 08.08.2012
comment
Это просто - простая рекурсия с базовыми случаями. Если хочешь, я могу помочь тебе. - person barak1412; 08.08.2012
comment
@ barak1412 Ваше решение действительно очень хорошее. Он имеет временную сложность O (9 * 4 * n). Можно ли еще оптимизировать. - person Luv; 09.08.2012
comment
Я думаю, вы говорите об оптимизации не с точки зрения сложности. Я постараюсь подумать об оптимизации. - person barak1412; 09.08.2012

Предполагая, что подпоследовательность должна состоять из последовательных цифр, вы можете сканировать слева направо, отслеживая, в каком порядке читаются последние 4 цифры. Таким образом, вы можете выполнить линейное сканирование и просто применить правила делимости.

Если цифры не обязательно идут подряд, вы можете немного поработать с таблицами поиска. Идея состоит в том, что вы можете создать трехмерный массив с именем table, в котором table[i][j][k] - это количество сумм из i цифр до индекса j, так что сумма оставляет остаток k при делении на 9. Сама таблица имеет размер 45n (i идет от 0 до 4, j от 0 до n-1, а k от 0 до 8).

Для рекурсии каждая table[i][j][k] запись опирается на table[i-1][j-1][x] и table[i][j-1][x] для всех x от 0 до 8. Поскольку каждое обновление записи занимает постоянное время (по крайней мере, относительно n), это должно дать вам время выполнения O (n).

person Dennis Meng    schedule 07.08.2012
comment
Числа не обязательно идут подряд в подпоследовательности. - person Luv; 08.08.2012
comment
Если вы ищете только общее количество, вы можете попробовать кое-что с мемоизацией; Я обновлю свой ответ. (Раньше я был глупым) - person Dennis Meng; 08.08.2012
comment
Кстати, не могли бы вы объяснить мне, как это O (n ^ 3) или O (n ^ 4)? - person Vikram; 08.08.2012
comment
@Vikram Первоначально я работал в предположении, что мы должны были сгенерировать каждую группу из 4 цифр (из которых n выберите 4), что сделало бы это O (n ^ 4). Затем я нашел более разумный подход к этому. - person Dennis Meng; 08.08.2012
comment
@DennisMeng См. Мой рабочий код для вышеуказанной проблемы с использованием DP, он работает в линейное время с использованием таблиц поиска, но в худшем случае занимает O (600n) и пространство O (n * 4 * 10). Пожалуйста, посмотрите, можно ли его оптимизировать. Спасибо. - person Luv; 08.08.2012

Как насчет этого:

/*NOTE: The following holds true, if the subsequences consist of digits in contagious locations */ 

public int countOccurrences (String s) {
    int count=0;
    int len = s.length();
    String subs = null;
    int sum;

    if (len < 4)
        return 0;
    else {
        for (int i=0 ; i<len-3 ; i++) {
            subs = s.substring(i, i+4);
            sum = 0;

            for (int j=0; j<=3; j++) {
                sum += Integer.parseInt(String.valueOf(subs.charAt(j)));
            }

            if (sum%9 == 0)
                count++;
        }           
        return count;
    }   
}
person Vikram    schedule 07.08.2012
comment
Как упоминалось в комментарии к моему вопросу, подпоследовательности не обязательно содержат последовательные цифры. В противном случае это должно сработать. - person Dennis Meng; 08.08.2012

Вот полный рабочий код для вышеупомянутой проблемы, основанный на описанных выше способах использования таблиц поиска.

int fun(int h)
{
return (h/10 + h%10);
}

int main()
{
int t;
scanf("%d",&t);
int i,T;
for(T=0;T<t;T++)
{
    char str[10001];
    scanf("%s",str);        
    int len=strlen(str);
    int arr[len][5][10];
    memset(arr,0,sizeof(int)*(10*5*len));

    int j,k,l;
        for(j=0;j<len;j++)
            {
              int y;
                y=(str[j]-48)%10;
                arr[j][1][y]++;
            }



        //printarr(arr,len);    





        for(i=len-2;i>=0;i--)   //represents the starting index of the string
            {


                    int temp[5][10];
                    //COPYING ARRAY
                    int a,b,c,d;
                    for(a=0;a<=4;a++)
                    for(b=0;b<=9;b++)
                    temp[a][b]=arr[i][a][b]+arr[i+1][a][b];


            for(j=1;j<=4;j++)   //represents the length of the string
                {
                for(k=0;k<=9;k++)   //represents the no. of ways to make it
                    {

                        if(arr[i+1][j][k]!=0)
                          {
                          for(c=1;c<=4;c++)
                            {
                              for(d=0;d<=9;d++)
                                 {
                            if(arr[i][c][d]!=0)
                                 {
                                int h,r;
                                r=j+c;
                                if(r>4)
                                continue;
                                h=k+d;                      
                                h=fun(h);
                                if(r<=4)
                                  temp[r][h]=( temp[r][h]+(arr[i][c][d]*arr[i+1][j][k]))%1000000007;
                              }}}
                        }






                    //copy back from temp array

                    }
                }
                for(a=0;a<=4;a++)
                    for(b=0;b<=9;b++)
                    arr[i][a][b]=temp[a][b];
            }








printf("%d\n",(arr[0][1][9])%1000000007);


}


    return 0;
}   
person Luv    schedule 08.08.2012
comment
Он выполняется за линейное время с использованием таблиц поиска, но в худшем случае занимает O (600n) и пространство O (n * 4 * 10). Пожалуйста, посмотрите, можно ли его еще оптимизировать. заранее спасибо - person Luv; 08.08.2012