Câu hỏi phỏng vấn: Kiểm tra xem một chuỗi có phải là một vòng quay của chuỗi khác [đã đóng]


235

Một người bạn của tôi đã được hỏi câu hỏi sau đây hôm nay khi phỏng vấn cho vị trí nhà phát triển phần mềm:

Đưa ra hai chuỗi s1s2làm thế nào bạn sẽ kiểm tra nếu s1là một phiên bản xoay của s2?

Thí dụ:

Nếu s1 = "stackoverflow"sau đó là một số phiên bản xoay của nó:

"tackoverflows"
"ackoverflowst"
"overflowstack"

nơi như "stackoverflwo"không một phiên bản xoay.

Câu trả lời anh đưa ra là:

Lấy s2và tìm tiền tố dài nhất là một chuỗi con của s1, nó sẽ cung cấp cho bạn điểm xoay. Khi bạn tìm thấy điểm đó, hãy dừng lại s2ở điểm đó để lấy s2as2bsau đó chỉ cần kiểm tra xemconcatenate(s2a,s2b) == s1

Có vẻ như một giải pháp tốt cho tôi và bạn tôi. Nhưng người phỏng vấn lại nghĩ khác. Ông yêu cầu một giải pháp đơn giản hơn. Xin hãy giúp tôi bằng cách cho biết làm thế nào bạn sẽ làm điều này trongJava/C/C++ ?

Cảm ơn trước.


4
Bạn không phải kiểm tra xem concatenate (s2a, s2b) == s1, bởi vì bạn biết s2a bằng với đầu của s1. Bạn chỉ có thể kiểm tra xem s2b == chuỗi con của s1 từ rotation_point đến hết.
Jason Hall

33
Làm thế nào mà câu hỏi này và câu trả lời hàng đầu nhận được rất nhiều upvote!?
David Johnstone

9
@David: Bởi vì nó thú vị.
Cam

6
Tôi sẽ nói, rất thú vị và một câu trả lời đơn giản, thanh lịch.

7
@David: bởi vì đây là câu hỏi chưa được hỏi trước đây và cũng là câu hỏi mà mọi người đều hiểu (nếu không hiểu câu hỏi / câu trả lời, người ta thường chắc chắn sẽ không đưa ra câu hỏi; một câu hỏi khá đơn giản có đối tượng rộng hơn) và cả bởi vì điều này đã được gắn thẻ với cả Java và C. Nó được tính :)
BalusC

Câu trả lời:


687

Đầu tiên hãy chắc chắn s1s2có cùng độ dài. Sau đó kiểm tra xem có phải s2là một chuỗi con được s1nối với 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

Trong Java:

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

49
Tôi thích sự thanh lịch của nó, nhưng tôi đã phải suy nghĩ một lúc để kiểm tra xem có bất kỳ sự tích cực sai lầm nào không. (Tôi không nghĩ là có.)
Jon Skeet

6
Bạn cũng có thể sử dụng (s1+s1).contains(s2)trong Java.
đa gen

4
Dù sao tôi sẽ phản đối một chút về điều này như một câu hỏi phỏng vấn. Nó có một "aha!" thành phần, tôi nghĩ. Hầu hết các lập trình viên (bao gồm cả tôi) sẽ chỉ sử dụng vũ lực, điều này không phải là không hợp lý, và điều đó có thể cảm thấy không đủ "thông minh" với người phỏng vấn.
Daniel Daranas

5
@Jon Tập trung vào s1+s1. Rõ ràng, tất cả các chuỗi con của nó với kích thước s1.lengthlà xoay s1, bằng cách xây dựng. Do đó, bất kỳ chuỗi kích thước s1.lengthnào là một chuỗi con s1+s1phải là một vòng quay của s1.
Daniel C. Sobral

6
@unicornaddict - Điều tuyệt vời về giải pháp này là quá rõ ràng một khi bạn chỉ ra, tôi ghét bản thân mình vì đã không nghĩ về nó!
James B

101

Chắc chắn một câu trả lời tốt hơn sẽ là, "Chà, tôi đã hỏi cộng đồng stackoverflow và có lẽ sẽ có ít nhất 4 câu trả lời thực sự tốt trong vòng 5 phút". Não bộ là tốt và tất cả, nhưng tôi đặt một giá trị cao hơn cho một người biết cách làm việc với người khác để có được giải pháp.


14
+1 cho má tuyệt đối. Thực hiện ngày của tôi :-)
Platinum Azure

5
Nếu họ không đồng ý, thì bạn có thể liên kết họ với câu hỏi này.
Cam

51
Việc lấy điện thoại của bạn ra trong một cuộc phỏng vấn có thể bị coi là thô lỗ và cuối cùng họ sẽ thuê Jon Skeet.
tstenner

2
Đó thực sự có thể là chính xác những gì tôi đã nói
Chris Dutrow

6
Tôi không nghĩ họ sẽ đủ khả năng để Jon Skeet.
SolutionYogi

49

Một ví dụ khác về python (dựa trên câu trả lời):

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

1
Thật thú vị, tôi đã nghĩ về sự trùng lặp s2thay vì s1quá ... sau đó nhận ra rằng mối quan hệ là đối xứng.
Matthieu M.

1
Nếu chuỗi có thể dài, thì đây là phiên bản Python sử dụng Boyer-Moore để có thời gian chạy O (n): def isrotation (s1, s2): return len (s1) == len (s2) và re.compile (re .escape (s1)). tìm kiếm (2 * s2) không phải là Không có
Duncan

2
@Duncan: intoán tử không sử dụng thuật toán O (n)?
Ken Bloom

1
@Duncan: Các phương thức chuỗi python sử dụng Boyer-Moore-Horspool được tối ưu hóa. Tôi tự hỏi nếu Java có tối ưu hóa tương tự.
Thomas Ahle

1
@Thomas cảm ơn vì đã chỉ ra điều đó. Tôi đã nghĩ rằng chỉ những biểu thức thông thường mới sử dụng Boyer-Moore nhưng tôi thấy tôi đã sai. Đối với Python 2.4 và trước đó câu trả lời của tôi là đúng nhưng vì Python 2.5 s1 in s2được tối ưu hóa. Xem effbot.org/zone/opeslib.htm để biết mô tả về thuật toán. Google dường như chỉ ra rằng Java không có tìm kiếm chuỗi nhanh (xem johannburkard.de/software/stringsearch chẳng hạn) mặc dù tôi nghi ngờ nó sẽ phá vỡ mọi thứ nếu họ thay đổi.
Duncan

32

Vì những người khác đã gửi giải pháp phức tạp thời gian trường hợp xấu nhất bậc hai, tôi sẽ thêm một giải pháp tuyến tính (dựa trên Thuật toán 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;
}

ví dụ làm việc


5
+1 cho ideone.com - có vẻ rất thú vị!
Martin Vseticka

25

EDIT: Câu trả lời được chấp nhận rõ ràng là thanh lịch và hiệu quả hơn so với điều này, nếu bạn phát hiện ra nó. Tôi đã để lại câu trả lời này như những gì tôi sẽ làm nếu tôi không nghĩ sẽ nhân đôi chuỗi ban đầu.


Tôi chỉ vũ phu thôi. Kiểm tra độ dài trước, sau đó thử mọi độ lệch xoay có thể có. Nếu không có cái nào trong số chúng hoạt động, trả về false - nếu bất kỳ cái nào trong số chúng làm, hãy trả về true ngay lập tức.

Không có nhu cầu đặc biệt để nối - chỉ cần sử dụng các con trỏ (C) hoặc chỉ mục (Java) và đi cả hai, mỗi chuỗi trong một chuỗi - bắt đầu từ đầu một chuỗi và bù vòng xoay ứng viên hiện tại trong chuỗi thứ hai và gói khi cần thiết . Kiểm tra sự bằng nhau của ký tự tại mỗi điểm trong chuỗi. Nếu bạn đến cuối chuỗi đầu tiên, bạn đã hoàn thành.

Nó có thể sẽ dễ dàng ghép nối - mặc dù có lẽ kém hiệu quả hơn, ít nhất là trong Java.


8
+1 - chúng tôi không cần giải pháp thanh lịch chạy trong hơn 3 lần giải pháp hiệu quả nhất. Đây là C ... tối ưu hóa vi mô là de Riguer .
Stephen C

8
Người phỏng vấn: Lotta nói chuyện, nhưng tôi cá rằng anh chàng này không thể viết mã.
Humphrey Bogart

8
@Beau: Nếu bất cứ ai muốn nghĩ điều đó, họ đều vui lòng hỏi tôi mã. Nếu ai đó chỉ hỏi tôi "tôi sẽ làm gì đó" thì tôi thường mô tả thuật toán thay vì nhảy vào mã.
Jon Skeet

3
@Jon - Tôi đọc bình luận của Beau như một trò đùa
oxbow_lakes

37
@Jon Đó là một trò đùa! Người phỏng vấn không phỏng vấn Jon Skeet, Jon Skeet phỏng vấn anh ta.
Humphrey Bogart

17

Đây là một cách sử dụng regex chỉ để giải trí:

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

Bạn có thể làm cho nó đơn giản hơn một chút nếu bạn có thể sử dụng một ký tự phân cách đặc biệt được đảm bảo không nằm trong một trong hai chuỗi.

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

Bạn cũng có thể sử dụng lookbehind với sự lặp lại hữu hạn thay thế:

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

6
+1 để trở thành bậc thầy regex.
Chris Thornton

-1 Để đặt các từ "regex" và "vui vẻ" trong cùng một tuyên bố, mà không sửa đổi "vui vẻ" bằng "không" (chỉ nói đùa, tôi đã không bỏ phiếu)
Binary Worrier

-3 cho ngụ ý rằng regexes không vui.
manlycode

bất kỳ ai cũng có thể giải thích cách regex này "(. *) (. *) = \\ 2 \\ 1" hoạt động!
mawia

10

Whoa, whoa ... tại sao mọi người lại rất hồi hộp với O(n^2)câu trả lời? Tôi khẳng định rằng chúng ta có thể làm tốt hơn ở đây. Câu trả lời ở trên bao gồm một O(n)thao tác trong một O(n)vòng lặp (lệnh gọi chuỗi con / indexOf). Ngay cả với một thuật toán tìm kiếm hiệu quả hơn; nói Boyer-Moorehay KMP, trường hợp xấu nhất vẫn là O(n^2)với các bản sao.

Một O(n)câu trả lời ngẫu nhiên là đơn giản; lấy một hàm băm (như dấu vân tay Rabin) hỗ trợ O(1)cửa sổ trượt; băm chuỗi 1, sau đó băm chuỗi 2 và tiến hành di chuyển cửa sổ cho hàm băm 1 xung quanh chuỗi và xem các hàm băm có va chạm không.

Nếu chúng ta tưởng tượng trường hợp xấu nhất là "quét hai chuỗi DNA", thì xác suất va chạm sẽ tăng lên và điều này có thể suy biến thành một thứ gì đó giống như O(n^(1+e))hoặc một cái gì đó (chỉ cần đoán ở đây).

Cuối cùng, có một O(nlogn)giải pháp xác định có hằng số rất lớn bên ngoài. Về cơ bản, ý tưởng là lấy một tổ hợp của hai chuỗi. Giá trị tối đa của tích chập sẽ là chênh lệch xoay (nếu chúng được xoay); một O(n)kiểm tra xác nhận. Điều tuyệt vời là nếu có hai giá trị tối đa bằng nhau, thì cả hai cũng là giải pháp hợp lệ. Bạn có thể thực hiện tích chập với hai sản phẩm FFT và một sản phẩm chấm và iFFT, do đó nlogn + nlogn + n + nlogn + n == O(nlogn).

Vì bạn không thể đệm bằng số 0 và bạn không thể đảm bảo rằng các chuỗi có độ dài 2 ^ n, các FFT sẽ không phải là chuỗi nhanh; chúng sẽ là những cái chậm, O(nlogn)nhưng vẫn là một hằng số lớn hơn nhiều so với thuật toán CT.

Tất cả những gì đã nói, tôi hoàn toàn, 100% tích cực rằng có một O(n)giải pháp xác định ở đây, nhưng thật đáng kinh ngạc nếu tôi có thể tìm thấy nó.


KMP trên chuỗi nối liền với chính nó (có thể là vật lý hoặc hầu như với a %stringsize) được đảm bảo là thời gian tuyến tính.
Kragen Javier Sitaker

+1 cho Rabin-Karp. Không giống như KMP, nó sử dụng không gian không đổi và thực hiện đơn giản hơn. . giải pháp lượng tử - nhưng bây giờ đang trở nên ngớ ngẩn, phải không?
Darius Bacon

1
RK không đưa ra giải pháp O (n) xác định và KMP là O (n) trong không gian có thể không mong muốn. Tra cứu chuỗi con Two Way hoặc SMOA, cả O (n) trong thời gian và O (1) trong không gian. Nhân tiện, glibc strstr sử dụng Two Way, nhưng nếu bạn thực sự nối các chuỗi để sử dụng nó trái ngược với việc sử dụng% len, bạn sẽ quay lại O (n) trong không gian. :-)
R .. GitHub DỪNG GIÚP ICE

8

Nắm tay, đảm bảo 2 chuỗi có cùng độ dài. Sau đó, trong C, bạn có thể làm điều này với một lần lặp con trỏ đơn giản.


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
Ah, C. Tại sao phải làm gì đó trong một nửa thời gian và mã khi bạn có thể làm điều đó trong C!
Humphrey Bogart

11
+1 Nó được viết rất tốt C. Và để công bằng, câu hỏi được gắn thẻ 'c'.
Nick Moore

5
Trong mã này, bạn đã đi các chuỗi ít nhất 2 lần nếu không 3 lần (theo strlen và strcmp). Bạn có thể tự lưu kiểm tra này và bạn có thể giữ logic đó trong vòng lặp của mình. Khi bạn đang lặp, nếu một chuỗi ký tự chuỗi khác với chuỗi khác, hãy thoát khỏi vòng lặp. Bạn sẽ biết độ dài, như bạn đã biết bắt đầu và bạn biết khi nào bạn đã kết thúc null terminator.
Nasko

12
@Beau Martinez - bởi vì đôi khi thời gian thực hiện quan trọng hơn thời gian phát triển :-)
phkahler

2
@phkahler - Vấn đề là nó có thể chậm hơn. Các hàm chỉ mục được tích hợp trong các ngôn ngữ khác thường sử dụng thuật toán tìm kiếm chuỗi nhanh như Boyer-Moore, Rabin-Karp hoặc Knuth-Morris-Pratt. Thật quá ngây thơ khi chỉ phát minh lại mọi thứ trong C, và cho rằng nó sẽ nhanh hơn.
Thomas Ahle

8

Đây là một O(n)và tại chỗ alghoritm. Nó sử dụng <toán tử cho các phần tử của chuỗi. Tất nhiên nó không phải của tôi. Tôi đã lấy nó từ đây (Trang web đang được đánh bóng. Tôi đã tình cờ thấy nó một lần trong quá khứ và tôi không thể tìm thấy một cái gì đó như thế bằng tiếng Anh, vì vậy tôi cho thấy những gì tôi có :)).

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) chỉ là sooooo nhiều hơn nữa sâu sắc từ một comp-sci điểm hơn bất kỳ phi O (n) giải pháp :)
SyntaxT3rr0r

4
+1 cho một giải pháp tối ưu về thời gian và gần tối ưu về kích thước mã (cả nhị phân và LoC). Câu trả lời này sẽ còn tốt hơn với một lời giải thích.
R .. GitHub DỪNG GIÚP ICE

Hoàn toàn khó hiểu. Chúng tôi cần một lời giải thích!
j_random_hacker

7

Tôi đoán nó tốt hơn để làm điều này trong Java:

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

Trong Perl tôi sẽ làm:

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

hoặc thậm chí tốt hơn bằng cách sử dụng hàm index thay vì regex:

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

1
Bạn quên \Qtrong /\Q$string2/.
Kragen Javier Sitaker

3
\Qtrích dẫn bất kỳ ký tự đặc biệt trong $string2. Nếu không có nó, .sẽ được coi là một vòng quay của bất kỳ chuỗi 1 ký tự nào.
jackrabbit

6

Không chắc đây có phải là phương pháp hiệu quả nhất hay không, nhưng nó có thể tương đối thú vị : biến đổi Burrows-Wheeler . Theo bài báo WP, tất cả các phép quay của đầu vào đều cho cùng một đầu ra. Đối với các ứng dụng như nén, điều này không được mong muốn, do đó, vòng quay ban đầu được chỉ định (ví dụ: bằng một chỉ mục; xem bài viết). Nhưng để so sánh độc lập xoay vòng, nó có vẻ lý tưởng. Tất nhiên, nó không nhất thiết là hiệu quả lý tưởng!


Vì phép biến đổi Burrows-Wheeler liên quan đến việc tính toán tất cả các phép quay của chuỗi, nên chắc chắn sẽ không tối ưu .. :-)
R .. GitHub DỪNG GIÚP ICE

6

Lấy mỗi ký tự làm biên độ và thực hiện một phép biến đổi Fourier rời rạc trên chúng. Nếu chúng chỉ khác nhau khi quay, phổ tần số sẽ giống nhau trong phạm vi làm tròn. Tất nhiên điều này là không hiệu quả trừ khi độ dài là 2, do đó bạn có thể thực hiện FFT :-)


Chúng tôi đã sử dụng điều này như một bài tập mã hóa thú vị, tôi không chắc chúng tôi có thể đánh giá điều đó;).
jayshao

FFT bị lạm dụng :) +1 từ tôi
Aamir

5

Chưa có ai đưa ra cách tiếp cận modulo, vì vậy đây là một:

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;
}

Đầu ra:

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 nhận thấy lỗ hổng trong mã của tôi ở trên. Nó lỗi khi ký tự đầu tiên trong chuỗi xảy ra hai lần trở lên. Ví dụ, stackoverflowthử nghiệm chống lại owstackoverflowkết quả là sai, khi nó phải đúng.

Cảm ơn piotr đã phát hiện ra lỗi.

Bây giờ, đây là mã đã sửa:

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

Đây là đầu ra:

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

Đây là cách tiếp cận lambda:

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

Đây là đầu ra phương pháp lambda:

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

Tôi không nghĩ câu trả lời của bạn là đúng vì int ndx = a.IndexOf (b [0]); sẽ chỉ hoạt động nếu không có các phần tử khác có cùng giá trị b [0] trong chuỗi.
piotr

cảm ơn vì đã chú ý đến lỗ hổng đã sửa nó ngay bây giờ
Michael Buen

3

Vì không ai đưa ra giải pháp C ++. nó đây

bool isRotation(string s1,string s2) {

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

Điểm đôi: bạn đang thực hiện nối chuỗi tương đối đắt tiền ngay cả khi độ dài không khớp; bạn có thể vượt qua s2 bằng tham chiếu const.
Tony Delroy

2

Thủ thuật xoay con trỏ đơn giản của Opera hoạt động, nhưng nó cực kỳ kém hiệu quả trong trường hợp xấu nhất trong thời gian chạy. Đơn giản chỉ cần tưởng tượng một chuỗi với nhiều ký tự lặp đi lặp lại dài, nghĩa là:

S1 = HELLOHELLOHELLO1HELLOHELLOHELLO2

S2 = HELLOHELLOHELLO2HELLOHELLOHELLO1

"Vòng lặp cho đến khi có sự không phù hợp, sau đó tăng dần một lần và thử lại" là một cách tiếp cận kinh khủng, tính toán.

Để chứng minh rằng bạn có thể thực hiện phương pháp nối trong đồng bằng C mà không cần quá nhiều nỗ lực, đây là giải pháp của tôi:

  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);
}

Đây là tuyến tính trong thời gian chạy, với chi phí sử dụng bộ nhớ O (n) trên đầu.

(Lưu ý rằng việc triển khai strstr () là dành riêng cho nền tảng, nhưng nếu đặc biệt chết não, luôn có thể được thay thế bằng một giải pháp thay thế nhanh hơn như thuật toán Boyer-Moore)


1
Bạn có biết bất kỳ nền tảng nào có strstr()trong O (n + m) không? Ngoài ra, nếu tiêu chuẩn (hoặc bất cứ điều gì khác) không đảm bảo cho bạn thời gian chạy tuyến tính strstr(), bạn không thể khẳng định rằng toàn bộ thuật toán có tính tương thích thời gian tuyến tính.
jpalecek

Đó là lý do tại sao tôi nói rằng nó có thể được thay thế bằng thuật toán Boyer-Moore, làm cho nó chạy trong thời gian tuyến tính.
RarrRarrRarr

Có một số vấn đề tiềm ẩn đối với phương pháp phân bổ của bạn s1SelfConcat: chỉ vì C9x mà C cho phép kích thước mảng thay đổi (mặc dù GCC đã cho phép nó lâu hơn nhiều) và bạn sẽ gặp rắc rối khi phân bổ các chuỗi lớn trên ngăn xếp. Yosef Kreinin đã viết một bài đăng trên blog rất thú vị về vấn đề này. Ngoài ra, giải pháp của bạn vẫn là thời gian bậc hai với Boyer-Moore; bạn muốn KMP.
Kragen Javier Sitaker


2

Tôi thích câu trả lời kiểm tra xem s2 có phải là chuỗi con của s1 được nối với s1 không.

Tôi muốn thêm một tối ưu hóa mà không mất đi sự thanh lịch của nó.

Thay vì nối các chuỗi, bạn có thể sử dụng chế độ xem tham gia (Tôi không biết ngôn ngữ khác, nhưng đối với C ++ Boost.Range cung cấp loại chế độ xem như vậy).

Vì việc kiểm tra xem một chuỗi có phải là một chuỗi con của một chuỗi khác có độ phức tạp trung bình tuyến tính hay không (độ phức tạp trong trường hợp xấu nhất là bậc hai), việc tối ưu hóa này sẽ cải thiện tốc độ trung bình là 2 lần.


2

Một câu trả lời thuần Java (sans null tests)

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

Và bây giờ cho một cái gì đó hoàn toàn khác nhau.

Nếu bạn muốn có một câu trả lời thực sự nhanh trong một số ngữ cảnh bị ràng buộc khi các chuỗi không xoay vòng với nhau

  • tính toán một số tổng kiểm tra dựa trên ký tự (như xored tất cả các ký tự) trên cả hai chuỗi. Nếu chữ ký khác nhau, các chuỗi không phải là xoay của nhau.

Đồng ý, nó có thể thất bại, nhưng rất nhanh để nói nếu các chuỗi không khớp và nếu chúng khớp với nhau, bạn vẫn có thể sử dụng thuật toán khác như nối chuỗi để kiểm tra.


1

Một giải pháp của Ruby dựa trên các câu trả lời:

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

1

Rất dễ dàng để viết bằng PHP bằng cách sử dụng strlenvà các strposhàm:

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

Tôi không biết gì strpos sử dụng nội bộ, nhưng nếu nó sử dụng KMP thì đây sẽ là tuyến tính theo thời gian.


1

Đảo ngược một trong các chuỗi. Lấy FFT của cả hai (coi chúng là các chuỗi số nguyên đơn giản). Nhân các kết quả với nhau theo điểm khôn ngoan. Chuyển đổi trở lại bằng cách sử dụng FFT nghịch đảo. Kết quả sẽ có một đỉnh đơn nếu các chuỗi là các phép quay của nhau - vị trí của các cực đại sẽ cho biết mức độ chúng được xoay đối với nhau.


0

Tại sao không phải là một cái gì đó như thế này?


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

Tất nhiên, bạn có thể viết hàm IndexOf () của riêng bạn; Tôi không chắc .NET sử dụng cách ngây thơ hay cách nhanh hơn.

Ngây thơ:


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;
}

Nhanh hơn:


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;
}

Chỉnh sửa: Tôi có thể có một số vấn đề ngoài lề; đừng cảm thấy muốn kiểm tra. ;)


0

Tôi sẽ làm điều này trong 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

Tham gia string1với string2và sử dụng thuật toán KMP để kiểm tra xemstring2 có mặt trong chuỗi mới được thành lập. Bởi vì độ phức tạp thời gian của KMP nhỏ hơn substr.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.