Подсчет изоморфных циклических сдвигов в струне

1

Я пытаюсь решить алгоритмическую проблему, для которой у меня есть решение O(n²) времени, O(n) памяти (см. Ниже) вместо решения времени и памяти O(n).

Задача состоит в том, чтобы подсчитать количество изоморфных циклических сдвигов для данной строки s. Циклический сдвиг представляет собой преобразование исходной строки, например, если 0 <= k < n (где n - длина строки):

cyclicShift(0) = s
cyclicShift(k) = s[k-1]...s[n-1]s[0]...s[k] if k > 0

Циклический сдвиг называется изоморфным, если он равен начальной строке. У меня такое ощущение, что строка может иметь такой циклический сдвиг, если она состоит в повторении шаблона, но я не могу это доказать. Если бы это было так, то проблема заключалась бы в том, чтобы найти этот шаблон, а затем вывести число изоморфных циклических сдвигов, исходя из длины рисунка и длины строки.

Мое текущее решение строит все циклические сдвиги и сравнивает их с исходной строкой, которая является операцией O(n) в цикле, ограниченной n, что приводит к сложности O(n²). Вот мой код в Java для справки:

public int solution(String S) {
    int count = 1;
    int n     = S.length();

    // We represent the string as a LinkedList to construct the next cyclic shift
    // from a given one with a O(1) time complexity
    List<Character> list = new LinkedList<>();
    for (int i=0 ; i<n ; i++) 
        list.add(S.charAt(i));

    Deque<Character> tmp = new LinkedList<>(list);
    for (int k=1 ; k<n ; k++) {
        tmp.addFirst(tmp.removeLast());
        // this test is O(n) so this solution is O(n^2)
        if (tmp.equals(list))
            count++;
    }

    return count;
}

У вас есть представление о том, как я могу решить эту проблему в отношении требования O(n)? Ответы могут быть в Java, Scala или псевдокоде.

Теги:
algorithm

2 ответа

3
Лучший ответ

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

Рассмотрим первые k символов исходной строки, по определению циклического сдвига они равны второму k символам исходной строки.

Теперь рассмотрим второй k символов исходной строки. Они будут равны третьим k символам исходной строки и так далее, пока вы не покажете, что строка состоит из шаблона из k символов, который повторяется n/k раз.

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

Один из способов сделать это - использовать функцию отказа KMP. Функция отказа сообщает вам самый длинный правильный префикс строки, которая соответствует позиции i. Если вы вычислите значение функции сбоя в конце строки, оно сообщит вам номер T, который является длиной правильного префикса, который соответствует суффиксу строки.

Например, рассмотрим строку ABCABCABC. Функция отказа будет:

-1 0 0 0 1 2 3 4 5 6

Таким образом, значение в конце строки равно 6, и это говорит нам о том, что повторяющийся шаблон имеет длину p = nT, в данном случае 9-6 = 3.

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

m=p
count=0
while(m<n)
   if n%m==0: count+=1
   m+=p

В целом это O (n) во времени и пространстве.

  • 0
    Объяснение и соответствующая ссылка немного сложны для меня, позвольте мне некоторое время изучить его! Спасибо за ваш подробный ответ
  • 0
    Извините, возможно, существует гораздо более простой способ решения этой проблемы - это только один способ, который действительно прост, если у вас уже есть функция KMP.
Показать ещё 2 комментария
0

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

Я легко реализовал функцию Python, которая находит длину наименьшего повторяющегося шаблона в строке. Я добавляю его здесь, потому что, если намного проще использовать KMP, как это предложил Питер де Риваз, однако его ответ также доказывает, что существование изоморфного циклического сдвига подразумевает, что строка состоит из повторяющегося шаблона, а также способ решения проблема, когда была найдена самая маленькая модель.

Даже если он написан на Python, я считаю, что этот код довольно легко понять:

def smallestPattern(s)
    (count,res) = (0,1)
    for i,ch in enumerate(s):
        if ch != line[count]:
            count = 0
            res  += 1
        else:
            count += 1
    return res
  • 0
    Я не уверен, что этот метод всегда будет работать, например, для 00110 00110 он должен обнаружить повторяющийся шаблон размера 5.
  • 0
    @PeterdeRivaz, который был так давно, я даже не могу вспомнить, что это была за line . Я должен найти этот кусок кода в более широком контексте
Показать ещё 5 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню