Питання щодо інтерв'ю: перевірте, чи одна рядок є обертанням іншої рядка [закрито]


235

Сьогодні моєму другові на співбесіді було задано таке питання на посаду розробника програмного забезпечення:

Дано два рядки s1і s2як ви перевірите, чи s1є поворотною версією s2?

Приклад:

Якщо s1 = "stackoverflow"в такому разі перераховуються деякі з його повернутих версій:

"tackoverflows"
"ackoverflowst"
"overflowstack"

де , як "stackoverflwo"це НЕ повернена версія.

Він відповів:

Візьміть s2і знайдіть найдовший префікс, який є підрядком s1, який дасть точку обертання. Як тільки ви знайдете цю точку, перервіться s2в цій точці, щоб отримати її, s2aа s2bпотім просто перевірте, чи немаєconcatenate(s2a,s2b) == s1

Це виглядає як хороше рішення для мене та мого друга. Але інтерв'юер думав інакше. Він попросив більш простого рішення. Будь ласка, допоможіть мені, розповівши, як би ви це зробили Java/C/C++?

Заздалегідь спасибі.


4
Вам не потрібно перевіряти, чи є об'єднаний (s2a, s2b) == s1, тому що ви знаєте, що s2a дорівнює початку s1. Ви можете просто перевірити, чи s2b == підрядок s1 від rotation_point до кінця.
Зал Джейсона

33
Як це питання та найкраща відповідь отримали стільки результатів !?
Девід Джонстоун

9
@David: Тому що це цікаво.
Cam

6
Я б сказав, дуже цікава та елегантна, проста відповідь.
Гуру

7
@David: тому що це питання, яке тут не задають раніше, а також його розуміють усі (якщо хтось не розуміє питання / відповіді, зазвичай його точно не підтверджують; досить просте запитання має широку аудиторію), а також тому що це позначено як Java, так і C. Це рахується :)
BalusC

Відповіді:


687

Спочатку переконайтеся , що s1і s2мають однакову довжину. Потім перевірте, чи s2є підрядка, s1об'єднана з s1:

algorithm checkRotation(string s1, string s2) 
  if( len(s1) != len(s2))
    return false
  if( substring(s2,concat(s1,s1))
    return true
  return false
end

На Java:

boolean isRotation(String s1,String s2) {
    return (s1.length() == s2.length()) && ((s1+s1).indexOf(s2) != -1);
}

49
Мені подобається її витонченість, але мені довелося деякий час подумати, щоб перевірити, чи не було помилкових позитивних результатів. (Я не думаю, що є.)
Джон Скіт

6
Ви також можете використовувати (s1+s1).contains(s2)в Java.
полігенмастильні матеріали

4
У будь-якому випадку я заперечую проти цього як питання інтерв'ю. У ньому є "ага!" компонент, я думаю. Більшість програмістів (включаючи мене) просто використовували б грубу силу, що все одно нерозумно, і це може здатися, що інтерв'ю не достатньо "розумним".
Даніель Даранас

5
@ Jon Concentrate on s1+s1. Зрозуміло, що всі його підрядки за розміром - s1.lengthце обертання s1за конструкцією. Тому будь-який рядок розміру, s1.lengthщо є підрядком, s1+s1повинен бути обертанням s1.
Даніель К. Собрал

6
@unicornaddict - що чудово в цьому рішенні, це так очевидно, як тільки ти його вказуєш, я ненавиджу себе за те, що не думаю про це!
Джеймс Б

101

Безумовно, кращою відповіддю буде: "Ну, я б попросив спільноту stackoverflow і, мабуть, мав би принаймні 4 справді гарних відповіді протягом 5 хвилин". Мозок - це добре, і все, але я б більше цінував того, хто вміє працювати з іншими, щоб знайти рішення.


14
+1 для чистої щоки. Зробив мій день :-)
Platinum Azure

5
Якщо вони не погодилися, ви можете потім зв’язати їх із цим питанням.
Кам

51
Вилучення вашого мобільного телефону під час інтерв'ю може вважатися грубим, і врешті-решт вони найнять Джона Скіта.
tstenner

2
Насправді, мабуть, саме те, що я сказав би
Кріс Датроу

6
Я не думаю, що вони зможуть дозволити собі Джон Скіта.
SolutionYogi

49

Ще один приклад пітона (на основі відповіді):

def isrotation(s1,s2):
     return len(s1)==len(s2) and s1 in 2*s2

1
Цікаво, що я думав про дублювання, s2а не s1надто ... тоді зрозумів, що відношення все одно симетричне.
Матьє М.

1
Якщо рядок може бути довгим, ось версія Python, яка використовує Boyer-Moore, щоб отримати час роботи (O) n: def isrotation (s1, s2): return len (s1) == len (s2) та re.compile (re пошук. (2 * s2) - Немає
Duncan

2
@Duncan: хіба inоператор не використовує алгоритм O (n)?
Кен Блум

1
@Duncan: Метод рядків python використовує оптимізований Boyer-Moore-Horspool. Цікаво, чи є у Java подібні оптимізації.
Томас Ейл

1
@Thomas дякую, що вказали на це. Я думав, що Боєр-Мур використовував лише регулярні вирази, але я бачу, що я помилявся. Для Python 2.4 і раніше моя відповідь була правильною, але оскільки Python 2.5 s1 in s2оптимізований. Див. Effbot.org/zone/stringlib.htm для опису алгоритму. Google, схоже, вказує на те, що у Java немає швидкого пошуку рядків (див., Наприклад, johannburkard.de/software/stringsearch ), хоча я сумніваюся, що вона щось порушить, якщо вони її змінить.
Дункан

32

Оскільки інші подали квадратичне рішення про складність у найгіршому випадку, я додам лінійне (на основі алгоритму KMP ):

bool is_rotation(const string& str1, const string& str2)
{
  if(str1.size()!=str2.size())
    return false;

  vector<size_t> prefixes(str1.size(), 0);
  for(size_t i=1, j=0; i<str1.size(); i++) {
    while(j>0 && str1[i]!=str1[j])
      j=prefixes[j-1];
    if(str1[i]==str1[j]) j++;
    prefixes[i]=j;
  }

  size_t i=0, j=0;
  for(; i<str2.size(); i++) {
    while(j>0 && str2[i]!=str1[j])
      j=prefixes[j-1];
    if(str2[i]==str1[j]) j++;
  }
  for(i=0; i<str2.size(); i++) {
    if(j>=str1.size()) return true;
    while(j>0 && str2[i]!=str1[j])
      j=prefixes[j-1];
    if(str2[i]==str1[j]) j++;
  }

  return false;
}

робочий приклад


5
+1 для ideone.com - це виглядає дуже цікаво!
Мартін Всетічка

25

РЕДАКТУВАННЯ: Прийнята відповідь явно є більш елегантною та ефективною, ніж ця, якщо ви її помітили. Я залишив цю відповідь як те, що б робив, якби не думав подвоїти початковий рядок.


Я б просто знущався. Спершу перевірте довжину, а потім спробуйте всі можливі зміщення обертання. Якщо жоден з них не виходить, поверніть помилкове - якщо хтось із них робить, негайно поверніть істину.

Немає особливої ​​необхідності поєднувати - просто використовуйте вказівники (C) або індекси (Java) і пройдіть обидва разом, по одному в кожній рядку - починаючи з початку одного рядка і зміщення обертання поточного кандидата у другому рядку, і загортаючи, де це необхідно . Перевірте рівність символів у кожній точці рядка. Якщо ви дістанетесь до кінця першого рядка, ви закінчите.

Зробити це, мабуть, було б настільки просто, хоча, мабуть, менш ефективно, принаймні на Java.


8
+1 - нам не потрібні елегантні рішення, які в 3+ разів працюють найефективніше. Це C ... мікрооптимізація є більш жорсткою .
Стівен С

8
Інтерв'юер: Лотта розмовляє, але я думаю, що цей хлопець не може кодувати.
Хамфрі Богарт

8
@Beau: Якщо хтось хоче подумати про це, він запрошує запитати мене в коді. Якщо хтось просто запитує мене "як би я щось зробив", я зазвичай описую алгоритм, а не стрибаю до коду.
Джон Скіт

3
@Jon - Я прочитав коментар
Боя

37
@Jon Це був жарт! Інтерв'юер не інтерв'ює Джон Скіта, Джон Скіт опитує його.
Хамфрі Богарт

17

Ось такий, який використовує регулярний гекс для розваги:

boolean isRotation(String s1, String s2) {
   return (s1.length() == s2.length()) && (s1 + s2).matches("(.*)(.*)\\2\\1");
}

Ви можете зробити це трохи простіше, якщо ви можете використовувати спеціальний символ-роздільник, який гарантовано не буде в жодних рядках.

boolean isRotation(String s1, String s2) {
   // neither string can contain "="
   return (s1 + "=" + s2).matches("(.*)(.*)=\\2\\1");
}

Ви також можете використовувати lookbehind з кінцевим повторенням замість цього:

boolean isRotation(String s1, String s2) {
   return (s1 + s2).matches(
      String.format("(.*)(.*)(?<=^.{%d})\\2\\1", s1.length())
   );
}

6
+1 за те, що він був майстром регулярних виразів.
Кріс Торнтон

-1 За те, що викладаєте слова "regex" і "fun" в одне і те ж висловлення, не змінюючи "fun" на "not" (лише жартую, я не відмовився від голосування)
Binary Worrier

-3 для того, що означає, що регулярні виразки не є задоволенням.
manlycode

Чи може будь-який орган PLZ пояснити, як працював цей вираз "(. *) (. *) = \\ 2 \\ 1"!
mawia

10

Уааааааааааааааааааааоааааааааааааааааааааааааооооооооооооооооооооооооооойй радість O(n^2)відповіді? Я впевнений, що тут ми можемо зробити краще. Відповідь вище включає O(n)операцію в O(n)циклі (виклик substring / indexOf). Навіть із більш ефективним алгоритмом пошуку; скажімо, Boyer-Mooreабо KMP, найгірше, все ще є O(n^2)дублікати.

O(n)Рандомізоване Відповідь проста; візьміть хеш (на зразок відбитка пальця Рабіна), який підтримує O(1)розсувне вікно; хеш-рядок 1, потім хеш-рядок 2, і переходимо до переміщення вікна для хеша 1 навколо рядка і дивимося, чи стикаються хеш-функції.

Якщо ми уявляємо, що найгірший випадок - це щось на кшталт "сканування двох ниток ДНК", то ймовірність зіткнень зростає, і це, мабуть, вироджується до чогось подібного O(n^(1+e))чи чогось (просто тут здогадуються).

Нарешті, є детерміноване O(nlogn)рішення, яке має дуже велику постійну зовні. В основному ідея полягає у згортанні двох струн. Максимальним значенням згортки буде різниця обертання (якщо вони обертаються); Н. O(n)перевірка підтверджує. Приємно те, що якщо є два рівні максимальні значення, то вони обидва є також дійсними рішеннями. Ви можете зробити згортку за допомогою двох FFT і точкових продуктів, і iFFT nlogn + nlogn + n + nlogn + n == O(nlogn).

Оскільки ви не можете прокладати нулі і не можете гарантувати, що рядки мають довжину 2 ^ n, FFT не будуть швидкими; вони будуть повільними, O(nlogn)але все ж набагато більшою константою, ніж алгоритм КТ.

Все, що говорив, я абсолютно впевнений, що на сьогодні детерміновані O(n)рішення є, але проклятий, якщо я можу його знайти.


KMP у рядку зв'язаного з самим собою (фізично або практично з a %stringsize) гарантовано буде лінійним часом.
Краген Хав'єр Сітакер

+1 для Рабіна-Карпа. На відміну від KMP, він використовує постійний простір, і це простіше в реалізації. (Це також перша відповідь, про яку я подумав, за лічені секунди, і це важко побачити "правильну" відповідь, тому що ця є там, і вона приємна.) Ваша ідея згортання нагадує мені алгоритм Шор - мені цікаво, чи є підлінійний квантове рішення - але це зараз стає дурним, правда?
Дарій Бекон

1
RK не дає детермінованого рішення O (n), а KMP - O (n) у просторі, який може бути небажаним. Знайдіть двосторонній або SMOA підрядковий пошук, який є O (n) у часі та O (1) у просторі. До речі, glibc strstr використовує Двосторонній, але якщо ви насправді об'єднуєте рядки, щоб використовувати його на відміну від використання% len, ви повернетесь до O (n) у просторі. :-)
R .. GitHub ЗАСТАНОВИТИ ДІЯ

8

Кулак, переконайтесь, що 2 струни мають однакову довжину. Тоді в C ви можете це зробити за допомогою простої ітерації вказівника.


int is_rotation(char* s1, char* s2)
{
  char *tmp1;
  char *tmp2;
  char *ref2;

  assert(s1 && s2);
  if ((s1 == s2) || (strcmp(s1, s2) == 0))
    return (1);
  if (strlen(s1) != strlen(s2))
    return (0);

  while (*s2)
    {
      tmp1 = s1;
      if ((ref2 = strchr(s2, *s1)) == NULL)
        return (0);
      tmp2 = ref2;
      while (*tmp1 && (*tmp1 == *tmp2))
        {
          ++tmp1;
          ++tmp2;
          if (*tmp2 == '\0')
            tmp2 = s2;
        }
      if (*tmp1 == '\0')
        return (1);
      else
        ++s2;
    }
  return (0);
}

19
Ах, C. Навіщо робити щось за половину часу та кодувати, коли це можна зробити на C!
Хамфрі Богарт

11
+1 Це дуже добре написано C. І якщо чесно, питання позначено "c".
Нік Мур

5
У цьому коді ви пройшли рядки щонайменше 2, якщо не 3 рази (у strlen та strcmp). Ви можете зберегти собі цей чек, і ви можете зберегти цю логіку у своєму циклі. Якщо ви циклуєте, якщо число символів рядка відрізняється від іншого, вийдіть з циклу. Ви будете знати довжини, як ви знаєте початок, і знаєте, коли потрапили на нульовий термінатор.
Насько

12
@Beau Martinez - адже іноді час виконання важливіший за час розробки :-)
phkahler

2
@phkahler - Справа в тому, що це може бути повільніше. Вбудовані функції індексу іншими мовами зазвичай використовують алгоритм швидкого пошуку рядків, наприклад, Бойєр-Мур, Рабін-Карп або Кнут-Морріс-Пратт. Це занадто наївно, щоб просто винаходити все в C і вважати, що це швидше.
Томас Ейл

8

Ось O(n)і на місці алгоритм. Він використовує <оператор для елементів рядків. Звичайно, це не моє. Я взяв його звідси (Сайт польською мовою. Я натрапив на нього колись в минулому, і я не міг знайти щось подібне зараз англійською мовою, тому показую, що маю :)).

bool equiv_cyc(const string &u, const string &v)
{
    int n = u.length(), i = -1, j = -1, k;
    if (n != v.length()) return false;

    while( i<n-1 && j<n-1 )
    {
        k = 1;
        while(k<=n && u[(i+k)%n]==v[(j+k)%n]) k++;
        if (k>n) return true;
        if (u[(i+k)%n] > v[(j+k)%n]) i += k; else j += k;
    }
    return false;
}

+1 ... O (n) - це просто sooooo набагато глибше з точки зору comp-sci, ніж будь-яке не O (n) рішення :)
SyntaxT3rr0r

4
+1 для оптимального за часом рішення та майже оптимального за розміром коду (як двійкові, так і LoC). Ця відповідь була б ще кращою з поясненням.
R .. GitHub СТОП ДОПОМОГАЙТЕ

Дуже неприємно. Нам потрібне пояснення!
j_random_hacker

7

Я думаю, що краще зробити це Java:

boolean isRotation(String s1,String s2) {
    return (s1.length() == s2.length()) && (s1+s1).contains(s2);
}

У Перлі я би зробив:

sub isRotation {
 my($string1,$string2) = @_;
 return length($string1) == length($string2) && ($string1.$string1)=~/$string2/;
}

а ще краще, використовуючи функцію індексу замість регулярного вираження:

sub isRotation {
 my($string1,$string2) = @_;
 return length($string1) == length($string2) && index($string2,$string1.$string1) != -1;
}

1
Ви забули \Qв /\Q$string2/.
Краген Хав'єр Сітакер

3
\Qцитує будь-яких спеціальних символів у $string2. Без нього .вважалося б обертанням будь-якого рядка з 1 символом.
jackrabbit

6

Не впевнений, що це найефективніший метод, але це може бути відносно цікаво : перетворення Берроуз-Вілер . Відповідно до статті WP, всі обертання вводу дають однаковий вихід. Для таких програм, як стиснення, це не бажано, тому оригінальне поворот вказується (наприклад, за допомогою індексу; див. Статтю). Але для простого незалежного обертання порівняння це звучить ідеально. Звичайно, це не обов'язково ідеально ефективно!


Оскільки перетворення Берроуса-Уілера передбачає обчислення всіх обертів рядка, це, безумовно, не буде оптимальним .. :-)
R .. GitHub ЗАСТАНІТЬ ДОПОМОГУ ДВІ

6

Візьміть кожен символ як амплітуду і виконайте на них дискретні перетворення Фур'є. Якщо вони відрізняються лише обертанням, спектри частот будуть однаковими до похибки округлення. Звичайно, це неефективно, якщо довжина не становить 2, щоб ви могли зробити FFT :-)


Ми використали це як цікаву вправу кодування, я не впевнений, що ми змогли б це оцінити;).
jayshao

FFT зловживали :) +1 від мене
Аамір

5

Ще ніхто не пропонував модульний підхід, ось ось такий:

static void Main(string[] args)
{
    Console.WriteLine("Rotation : {0}",
        IsRotation("stackoverflow", "ztackoverflow"));
    Console.WriteLine("Rotation : {0}",
        IsRotation("stackoverflow", "ackoverflowst"));
    Console.WriteLine("Rotation : {0}",
        IsRotation("stackoverflow", "overflowstack"));
    Console.WriteLine("Rotation : {0}",
        IsRotation("stackoverflow", "stackoverflwo"));
    Console.WriteLine("Rotation : {0}",
        IsRotation("stackoverflow", "tackoverflwos"));
    Console.ReadLine();
}

public static bool IsRotation(string a, string b)
{
    Console.WriteLine("\nA: {0} B: {1}", a, b);

    if (b.Length != a.Length)
        return false;

    int ndx = a.IndexOf(b[0]);
    bool isRotation = true;
    Console.WriteLine("Ndx: {0}", ndx);
    if (ndx == -1) return false;
    for (int i = 0; i < b.Length; ++i)
    {
        int rotatedNdx = (i + ndx) % b.Length;
        char rotatedA = a[rotatedNdx];

        Console.WriteLine( "B: {0} A[{1}]: {2}", b[i], rotatedNdx, rotatedA );

        if (b[i] != rotatedA)
        {
            isRotation = false;
            // break; uncomment this when you remove the Console.WriteLine
        }
    }
    return isRotation;
}

Вихід:

A: stackoverflow B: ztackoverflow
Ndx: -1
Rotation : False

A: stackoverflow B: ackoverflowst
Ndx: 2
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: o A[11]: o
B: w A[12]: w
B: s A[0]: s
B: t A[1]: t
Rotation : True

A: stackoverflow B: overflowstack
Ndx: 5
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: o A[11]: o
B: w A[12]: w
B: s A[0]: s
B: t A[1]: t
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
Rotation : True

A: stackoverflow B: stackoverflwo
Ndx: 0
B: s A[0]: s
B: t A[1]: t
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: w A[11]: o
B: o A[12]: w
Rotation : False

A: stackoverflow B: tackoverflwos
Ndx: 1
B: t A[1]: t
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: w A[11]: o
B: o A[12]: w
B: s A[0]: s
Rotation : False

[EDIT: 2010-04-12]

piotr помітив недолік у моєму коді вище. Він помиляється, коли перший символ у рядку виникає двічі або більше. Наприклад, stackoverflowтестування на owstackoverflowрезультат призвело до помилок, коли це повинно бути правдою.

Дякуємо piotr за виявлення помилки.

Тепер ось виправлений код:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace TestRotate
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "ztackoverflow"));
            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "ackoverflowst"));
            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "overflowstack"));
            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "stackoverflwo"));
            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "tackoverflwos"));

            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "owstackoverfl"));

            Console.ReadLine();
        }

        public static bool IsRotation(string a, string b)
        {
            Console.WriteLine("\nA: {0} B: {1}", a, b);

            if (b.Length != a.Length)
                return false;

            if (a.IndexOf(b[0]) == -1 )
                return false;

            foreach (int ndx in IndexList(a, b[0]))
            {
                bool isRotation = true;

                Console.WriteLine("Ndx: {0}", ndx);

                for (int i = 0; i < b.Length; ++i)
                {
                    int rotatedNdx = (i + ndx) % b.Length;
                    char rotatedA = a[rotatedNdx];

                    Console.WriteLine("B: {0} A[{1}]: {2}", b[i], rotatedNdx, rotatedA);

                    if (b[i] != rotatedA)
                    {
                        isRotation = false;
                        break;
                    }
                }
                if (isRotation)
                    return true;
            }
            return false;
        }

        public static IEnumerable<int> IndexList(string src, char c)
        {
            for (int i = 0; i < src.Length; ++i)
                if (src[i] == c)
                    yield return i;
        }

    }//class Program
}//namespace TestRotate

Ось результат:

A: stackoverflow B: ztackoverflow
Rotation : False

A: stackoverflow B: ackoverflowst
Ndx: 2
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: o A[11]: o
B: w A[12]: w
B: s A[0]: s
B: t A[1]: t
Rotation : True

A: stackoverflow B: overflowstack
Ndx: 5
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: o A[11]: o
B: w A[12]: w
B: s A[0]: s
B: t A[1]: t
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
Rotation : True

A: stackoverflow B: stackoverflwo
Ndx: 0
B: s A[0]: s
B: t A[1]: t
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: w A[11]: o
Rotation : False

A: stackoverflow B: tackoverflwos
Ndx: 1
B: t A[1]: t
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
B: w A[11]: o
Rotation : False

A: stackoverflow B: owstackoverfl
Ndx: 5
B: o A[5]: o
B: w A[6]: v
Ndx: 11
B: o A[11]: o
B: w A[12]: w
B: s A[0]: s
B: t A[1]: t
B: a A[2]: a
B: c A[3]: c
B: k A[4]: k
B: o A[5]: o
B: v A[6]: v
B: e A[7]: e
B: r A[8]: r
B: f A[9]: f
B: l A[10]: l
Rotation : True

Ось лямбда-підхід:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace IsRotation
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "ztackoverflow"));

            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "ackoverflowst"));

            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "overflowstack"));
            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "stackoverflwo"));

            Console.WriteLine("Rotation : {0}",
                IsRotation("stackoverflow", "owstackoverfl"));

            string strToTestFrom = "stackoverflow";
            foreach(string s in StringRotations(strToTestFrom))
            {
                Console.WriteLine("is {0} rotation of {1} ? {2}",
                    s, strToTestFrom,
                    IsRotation(strToTestFrom, s) );
            }
            Console.ReadLine();
        }

        public static IEnumerable<string> StringRotations(string src)
        {
            for (int i = 0; i < src.Length; ++i)
            {
                var sb = new StringBuilder();
                for (int x = 0; x < src.Length; ++x)
                    sb.Append(src[(i + x) % src.Length]);

                yield return sb.ToString();
            }
        }

        public static bool IsRotation(string a, string b)
        {
            if (b.Length != a.Length || a.IndexOf(b[0]) < 0 ) return false;
            foreach(int ndx in IndexList(a, b[0]))
            {
                int i = ndx;
                if (b.ToCharArray().All(x => x == a[i++ % a.Length]))
                    return true;
            }
            return false;
        }

        public static IEnumerable<int> IndexList(string src, char c)
        {
            for (int i = 0; i < src.Length; ++i)
                if (src[i] == c)
                    yield return i;
        }

    }//class Program

}//namespace IsRotation

Ось вихід лямбда-підходу:

Rotation : False
Rotation : True
Rotation : True
Rotation : False
Rotation : True
is stackoverflow rotation of stackoverflow ? True
is tackoverflows rotation of stackoverflow ? True
is ackoverflowst rotation of stackoverflow ? True
is ckoverflowsta rotation of stackoverflow ? True
is koverflowstac rotation of stackoverflow ? True
is overflowstack rotation of stackoverflow ? True
is verflowstacko rotation of stackoverflow ? True
is erflowstackov rotation of stackoverflow ? True
is rflowstackove rotation of stackoverflow ? True
is flowstackover rotation of stackoverflow ? True
is lowstackoverf rotation of stackoverflow ? True
is owstackoverfl rotation of stackoverflow ? True
is wstackoverflo rotation of stackoverflow ? True

Я не думаю, що ваша відповідь є правильною, оскільки int ndx = a.IndexOf (b [0]); буде працювати лише в тому випадку, якщо в рядку немає інших елементів з однаковим значенням b [0].
piotr

дякую за те, що помітили недолік. виправлено це зараз
Майкл Буен

3

Як ніхто не дав рішення C ++. ось воно:

bool isRotation(string s1,string s2) {

  string temp = s1;
  temp += s1;
  return (s1.length() == s2.length()) && (temp.find(s2) != string::npos);
}

Пара балів: ви робите відносно дороге об'єднання рядків, навіть якщо довжини не збігаються; ви можете передати s2 за посиланням const.
Тоні Делрой

2

Простий трюк обертання вказівника Opera працює, але він є вкрай неефективним у гіршому випадку під час роботи. Просто уявіть рядок з багатьма довгими повторами символів, тобто:

S1 = HELLOHELLOHELLO1HELLOHELLOHELLO2

S2 = HELLOHELLOHELLO2HELLOHELLOHELLO1

"Цикл, поки не буде невідповідності, потім приріст по одному і повторити спробу" - це жахливий підхід, обчислювальний.

Щоб довести, що ви можете зробити підхід конкатенації в звичайній C без особливих зусиль, ось моє рішення:

  int isRotation(const char* s1, const char* s2) {
        assert(s1 && s2);

        size_t s1Len = strlen(s1);

        if (s1Len != strlen(s2)) return 0;

        char s1SelfConcat[ 2 * s1Len + 1 ];

        sprintf(s1SelfConcat, "%s%s", s1, s1);   

        return (strstr(s1SelfConcat, s2) ? 1 : 0);
}

Це лінійно за часом роботи за рахунок використання O (n) пам'яті в режимі накладних витрат.

(Зверніть увагу, що реалізація strstr () є специфічною для платформи, але якщо це особливо мозок, завжди можна замінити на більш швидку альтернативу, таку як алгоритм Бойєра-Мура)


1
Чи знаєте ви будь-яку платформу, яка має strstr()O (n + m)? Крім того, якщо стандарт (або що-небудь інше) не гарантує вам лінійний час роботи strstr(), ви не можете стверджувати, що весь алгоритм має лінійну простоту часу.
jpalecek

Ось чому я сказав, що його можна замінити алгоритмом Бойєра-Мура, змусивши його працювати в лінійний час.
RarrRarrRarr

У вашому методі розподілу є кілька можливих проблем s1SelfConcat: це лише оскільки C9x дозволяє C змінювати розміри масивів (хоча GCC це дозволив набагато довше), і ви зіткнетеся з проблемою з виділенням великих рядків на стеку. Йосеф Крейнін написав надзвичайно кумедну публікацію в блозі про цю проблему. Крім того, ваше рішення - це ще квадратичний час з Бойєром-Муром; ви хочете KMP.
Краген Хав'єр Сітакер


2

Мені подобається відповідь, що перевіряє, чи s2 є підрядком s1, з'єднаним з s1.

Я хотів додати оптимізацію, яка не втрачає елегантності.

Замість об'єднання рядків ви можете використовувати перегляд об'єднання (я не знаю іншої мови, але для C ++ Boost.Range надайте такий вид переглядів).

Оскільки перевірка, якщо рядок є підрядком іншої, має лінійну середню складність (найгірша складність є квадратичною), така оптимізація повинна покращити швидкість в середньому в 2 рази.


2

Чиста відповідь на Java (без нульових перевірок)

private boolean isRotation(String s1,String s2){
    if(s1.length() != s2.length()) return false;
    for(int i=0; i < s1.length()-1; i++){
        s1 = new StringBuilder(s1.substring(1)).append(s1.charAt(0)).toString();
        //--or-- s1 = s1.substring(1) + s1.charAt(0)
        if(s1.equals(s2)) return true;
    }
    return false;
}

2

А тепер про щось зовсім інше.

Якщо ви хочете по-справжньому швидкої відповіді в якомусь обмеженому контексті, коли рядки не обертаються одна до одної

  • обчислити деяку контрольну суму на основі символів (наприклад, закреслення всіх символів) в обох рядках. Якщо підписи відрізняються, рядки не є обертанням один одного.

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


1

Інше рішення Рубі на основі в відповідь:

def rotation?(a, b); a.size == b.size and (b*2)[a]; end

1

Дуже легко писати в PHP за допомогою strlenта strposфункцій:

function isRotation($string1, $string2) {
    return strlen($string1) == strlen($string2) && (($string1.$string1).strpos($string2) != -1);
}

Я не знаю, що strposвикористовує внутрішньо, але якщо він використовує KMP, це буде лінійним у часі.


1

Переверніть одну з струн. Візьміть FFT обох (трактуючи їх як прості послідовності цілих чисел). Помножте результати разом точково. Трансформація спини за допомогою зворотного FFT. Результат матиме єдиний пік, якщо струни будуть обертаннями один одного - положення піку буде вказувати на те, наскільки вони обертаються один щодо одного.


0

Чому б не щось подібне?


//is q a rotation of p?
bool isRotation(string p, string q) {
    string table = q + q;    
    return table.IndexOf(p) != -1;
}

Звичайно, ви можете написати власну функцію IndexOf (); Я не впевнений, чи .NET використовує наївний або швидший спосіб.

Наївний:


int IndexOf(string s) {
    for (int i = 0; i < this.Length - s.Length; i++)
        if (this.Substring(i, s.Length) == s) return i;
    return -1;
}

Швидше:


int IndexOf(string s) {
    int count = 0;
    for (int i = 0; i < this.Length; i++) {
        if (this[i] == s[count])
            count++;
        else
            count = 0;
        if (count == s.Length)
            return i - s.Length;
    }
    return -1;
}

Редагувати: у мене можуть виникнути певні проблеми один за одним; не відчуваю, як перевіряти. ;)


0

Я б зробив це в Perl :

sub isRotation { 
     return length $_[0] == length $_[1] and index($_[1],$_[0],$_[0]) != -1; 
}

0
int rotation(char *s1,char *s2)
{
    int i,j,k,p=0,n;
    n=strlen(s1);
    k=strlen(s2);
    if (n!=k)
        return 0;
    for (i=0;i<n;i++)
    {
        if (s1[0]==s2[i])
        {
            for (j=i,k=0;k<n;k++,j++)
            {
                if (s1[k]==s2[j])
                    p++;
                if (j==n-1)
                    j=0;
            }
        }
    }
    if (n==p+1)
      return 1;
    else
      return 0;
}

0

Приєднуйтесь string1до string2та використовуйте алгоритм KMP, щоб перевірити наявність string2у новоствореному рядку. Тому що часова складність KMP менша, ніж substr.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.