лексикографически наименьшая строка после вращения

Я пытаюсь решить эту проблему в spoj

Мне нужно найти количество поворотов данной строки, которое сделает ее лексикографически наименьшей среди всех поворотов.

Например:

Оригинал: ama

Первый поворот: maa

Второй поворот: aam Это наименьшее лексикографическое вращение, поэтому ответ - 2.

Вот мой код:

string s,tmp;
    char ss[100002];
    scanf("%s",ss);
    s=ss;
    tmp=s;
    int i,len=s.size(),ans=0,t=0;
    for(i=0;i<len;i++)
    {
        string x=s.substr(i,len-i)+s.substr(0,i);
        if(x<tmp)
        {
            tmp=x;
            t=ans;
        }
        ans++;
    }

    cout<<t<<endl;

Я получаю сообщение «Превышен лимит времени» для этого решения. Я не понимаю, какие оптимизации можно сделать. Как я могу увеличить скорость моего решения?


person doctore    schedule 21.02.2013    source источник
comment
Пожалуйста, не используйте региональные сокращения, такие как «нет» и «плз». StackOverflow имеет глобальную аудиторию, многие из которых не являются носителями английского языка. Кроме того, что такое TLE?   -  person Robᵩ    schedule 21.02.2013
comment
Другие оптимизации? Кроме чего?   -  person n. 1.8e9-where's-my-share m.    schedule 21.02.2013
comment
Вы отвечаете не на тот вопрос. Связанный вопрос состоит в том, сколько поворотов, а не в том, какой лексикографически наименьший ответ.   -  person Watusimoto    schedule 21.02.2013
comment
@ Robᵩ TLE - это аббревиатура слова spoj, означающая Превышен лимит времени. Похоже, OP нужно ускорить решение.   -  person Gorpik    schedule 21.02.2013
comment
@Watusimoto: да ... мне нужно отсутствие поворотов, но для этого мне нужно решить, какая строка является лексикографически наименьшей   -  person doctore    schedule 21.02.2013
comment
@ Робо: я ценю твой совет .. спасибо   -  person doctore    schedule 21.02.2013
comment
См. Мой ответ для подробного объяснения рабочего кода.   -  person Abhijit Sarkar    schedule 12.04.2019


Ответы (2)


Вы можете использовать модифицированный массив суффиксов. Я имею в виду измененный, потому что вы не должны останавливаться на конце слова.

Вот код аналогичной проблемы, которую я решил ( SA - это массив суффиксов):

//719
//Glass Beads
//Misc;String Matching;Suffix Array;Circular
#include <iostream>
#include <iomanip>
#include <cstring>
#include <string>
#include <cmath>
#define MAX 10050
using namespace std;

int RA[MAX], tempRA[MAX];
int SA[MAX], tempSA[MAX];
int C[MAX];                

void suffix_sort(int n, int k) {
    memset(C, 0, sizeof C);        

    for (int i = 0; i < n; i++)        
        C[RA[(i + k)%n]]++;

    int sum = 0;
    for (int i = 0; i < max(256, n); i++) {                     
        int t = C[i]; 
        C[i] = sum; 
        sum += t;
    }

    for (int i = 0; i < n; i++)        
        tempSA[C[RA[(SA[i] + k)%n]]++] = SA[i];

    memcpy(SA, tempSA, n*sizeof(int));
}

void suffix_array(string &s) {             
    int n = s.size();

    for (int i = 0; i < n; i++) 
        RA[i] = s[i];              

    for (int i = 0; i < n; i++) 
        SA[i] = i;

    for (int k = 1; k < n; k *= 2) {     
        suffix_sort(n, k);
        suffix_sort(n, 0);

        int r = tempRA[SA[0]] = 0;
        for (int i = 1; i < n; i++) {
            int s1 = SA[i], s2 = SA[i-1];
            bool equal = true;
            equal &= RA[s1] == RA[s2];
            equal &= RA[(s1+k)%n] == RA[(s2+k)%n];

            tempRA[SA[i]] = equal ? r : ++r;     
        }

        memcpy(RA, tempRA, n*sizeof(int));
    } 
}

int main() {
    int tt; cin >> tt;
    while(tt--) {
        string s; cin >> s;
        suffix_array(s);
        cout << SA[0]+1 << endl;
   }
}

Я взял эту реализацию в основном из эту книгу. Есть более простая версия для записи O (n log²n), но она может быть недостаточно эффективной для вашего случая (n = 10 ^ 5). Эта версия - O (n log n), и это не самый эффективный алгоритм. В статье в Википедии перечислены некоторые O (n) алгоритмы, но я считаю, что большинство из них слишком сложны для написания во время соревнований по программированию. Этого O (n log n) обычно достаточно для большинства проблем.

Вы можете найти несколько слайдов, объясняющих концепцию массива суффиксов (от автора упомянутой книги) здесь.

person Juan Lopes    schedule 21.02.2013
comment
Я прошу вас объяснить построение массива суффиксов или дать мне ссылку, объясняющую его построение. - person doctore; 22.02.2013

Я знаю, что это происходит очень поздно, но я наткнулся на это в Google, когда искал еще более быстрый вариант этого алгоритма. Оказывается, хорошая реализация находится на github: https://gist.github.com/MaskRay/8803371

Он использует факторизацию Линдона. Это означает, что он многократно разбивает строку на лексикографически уменьшающиеся слова Lyndon. Слово Линдона - это строки, которые являются (одним из) минимальным вращением самих себя. Если выполнить это по кругу, в качестве последнего найденного слова Lyndon получится lms строки.

int lyndon_word(const char *a, int n)
{
  int i = 0, j = 1, k;
  while (j < n) {
    // Invariant: i < j and indices in [0,j) \ i cannot be the first optimum
    for (k = 0; k < n && a[(i+k)%n] == a[(j+k)%n]; k++);
    if (a[(i+k)%n] <= a[(j+k)%n]) {
      // if k < n
      //   foreach p in [j,j+k], s_p > s_{p-(j-i)}
      //   => [j,j+k] are all suboptimal
      //   => indices in [0,j+k+1) \ i are suboptimal
      // else
      //   None of [j,j+k] is the first optimum
      j += k+1;
    } else {
      // foreach p in [i,i+k], s_p > s_{p+(j-i)}
      // => [i,i+k] are all suboptimal
      // => [0,j) and [0,i+k+1) are suboptimal
      // if i+k+1 < j
      //   j < j+1 and indices in [0,j+1) \ j are suboptimal
      // else
      //   i+k+1 < i+k+2 and indices in [0,i+k+2) \ (i+k+1) are suboptimal
      i += k+1;
      if (i < j)
        i = j++;
      else
        j = i+1;
    }
  }
  // j >= n => [0,n) \ i cannot be the first optimum
  return i;
}
person Christoph Diegelmann    schedule 16.05.2016