Это единственная проблема повышенной сложности в разделе Жадные алгоритмы набора задач для подготовки к собеседованию Hackerrank. Ссылка здесь.

Эта проблема была сложной, и ей пришлось прибегать к помощи других пользователей. Хочу поделиться своими мыслями.

Проблема заключается в том, что вам дана строка s, которая является результатом следующих операций над некоторой строкой A. s = merge(reverse(A) ), shuffle(A))

Операции определяются следующим образом:

  • Слияние (A, B): объединяет две строки в одну, сохраняя при этом относительный порядок символов каждой строки.
  • Реверс (A): переворачивает струну A.
  • Перемешать (A): перемешивает символы A или оставляет их нетронутыми.

Нам нужно найти лексикографически наименьший A. Это означает, что A с наименьшим алфавитным порядком.

Решение

Ключ к этому в том, что, поскольку Merge сохраняет относительный порядок обеих строк. Наша строка A спрятана между повторяющимися символами в обратном порядке.

Псевдокод

  • Для начала нам нужно подсчитать появление символов.
  • Затем мы делаем копии вхождений, называя их пригодными для использования символами.
  • Затем мы половину вхождений и называем это нужными символами
  • Затем мы производим пустой счет для нашего решения.
  • И пустая строка для нашего решения
  • Справа налево в строке s
  • - Если персонаж требуется
  • - - мы отступаем, если можем, и улучшаем наше решение
  • - - добавляем иероглиф в раствор
  • - - добавляем к количеству решений
  • - - уменьшаем количество полезного использования
  • - еще
  • - - мы игнорируем и сокращаем количество используемых
  • вернуть наше решение

Он должен следовать этой логике:

s=eggegg
we iterate s right to left
- G is required We add G   Sol:{g_0}
- G is required We add G   Sol:{g_0, g_1}
- E is required We add E   Sol:{e_0}
-   we backtrack pop g_1, pop g_0
- G is required We add G   Sol:{e_0, g_1}
- G is required We add G   Sol:{e_0, g_1, g_2}
- E is not required        Sol:{e_0, g_1, g_2}
return Sol

Труднее всего знать, когда и сколько раз мы возвращаемся. Вот где полезно вести подсчет решения и используемых символов. Счетчик решения - это то, сколько символов будет в нашем решении. Подсчет используемых символов - это то, сколько символов можно использовать. Перед возвратом мы задаемся вопросом: смогу ли я создать требуемые символы, если уменьшу количество имеющихся у меня на единицу с учетом того, что осталось увидеть. Например, когда в примере мы вернулись к E. У нас было 2 g в решении, но мы еще не увидели 2 g, а требуемые g равны 2., поэтому мы спрашиваем.

Будет ли у меня 2 g минус один, который я собираюсь игнорировать, плюс те, которые я еще не видел, позволят мне сделать требуемые 2 g? Мы задаем один и тот же вопрос каждый раз, когда рассматриваем возможность возврата.

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

Код

Ссылка на код на гитхабе

static int wrd_count[26]={};
static int rem_count[26]={};
static int sol_count[26]={};
static char sol[10001];
string reverseShuffleMerge(string s){
    int n = s.size();
    int j = 0;
    const char* s_chars = s.c_str();
    
    for(int i = 0; i < n ; i++)
        wrd_count[s[i]-'a']++;
    
    memcpy(rem_count, wrd_count,26*(sizeof(int)) );
    
    for(int i = 0; i < 26 ; i++)
        wrd_count[i]/=2;
    
    char l_char;
    int  l_char_indx;
    
    for(int i = n-1; i >= 0; i--){
        
        l_char = s_chars[i];
        l_char_indx = l_char - 'a';
        if(i == n-1){
            sol[j] = l_char;
            j++;
            rem_count[l_char_indx]--;
            sol_count[l_char_indx]++;
            continue;
        }
        
        if(sol_count[ l_char_indx ] < wrd_count[l_char_indx]){
            
            if( l_char >= sol[j-1] ){
                sol[j] = l_char;
                j++;
                rem_count[l_char_indx]--;
                sol_count[l_char_indx]++;
            }else{
                while( j>0 && (l_char < sol[j-1]) && sol_count[sol[j-1]-'a']-1+ rem_count[sol[j-1]-'a'] >= (wrd_count[sol[j-1]-'a'])){
                    sol_count[sol[--j]-'a']--;
                }
                sol[j] = l_char;
                j++;
                rem_count[l_char_indx]--;
                sol_count[l_char_indx]++;
            }
            
        }else{
            rem_count[l_char_indx]--;
        }
        
    }
    
    sol[j] = '\0';
string sol_str(sol);
    
    return sol_str;
    
}