คำถามสัมภาษณ์: ตรวจสอบว่าสายหนึ่งมีการหมุนของสายอื่น ๆ [ปิด]


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 สิ้นสุดหรือไม่
Jason Hall

33
คำถามนี้และคำตอบที่ได้รับ upvotes มากมาย!
David Johnstone

9
@ David: เพราะมันน่าสนใจ
บแคม

6
ฉันจะบอกว่าน่าสนใจมากและเป็นคำตอบที่ง่ายและสง่างาม
ปราชญ์

7
@ David: เพราะมันเป็นคำถามที่ไม่ได้ถามมาก่อนและที่ทุกคน เข้าใจ (ถ้าใครไม่เข้าใจคำถาม / คำตอบมักจะไม่แน่ใจว่า upvote คำถามที่ค่อนข้างง่ายมีผู้ชมที่กว้างกว่า) และยัง เพราะนี่คือการติดแท็กที่มีทั้ง 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
ฉันชอบความสง่างามของมัน แต่ฉันต้องคิดทบทวนสักพักว่าไม่มีผลบวกใด ๆ (ฉันไม่คิดว่าจะมี)
Jon Skeet

6
คุณยังสามารถใช้(s1+s1).contains(s2)ใน Java
polygenelubricants

4
อย่างไรก็ตามฉันจะคัดค้านเล็กน้อยเพื่อเป็นคำถามสัมภาษณ์ มันมี "aha!" องค์ประกอบฉันคิดว่า โปรแกรมเมอร์ส่วนใหญ่ (ฉันรวมอยู่ด้วย) จะใช้กำลังดุร้ายซึ่งก็ไม่สมเหตุสมผลและนั่นอาจทำให้รู้สึกว่า "ฉลาด" ไม่พอสำหรับผู้สัมภาษณ์
Daniel Daranas

5
@ s1+s1จอนมีสมาธิในการ เห็นได้ชัดว่าสารตั้งต้นทั้งหมดที่มีขนาดs1.lengthเป็นการหมุนs1ด้วยการก่อสร้าง ดังนั้นสตริงใด ๆ ของขนาดs1.lengthที่เป็นย่อยของจะต้องเป็นการหมุนของs1+s1 s1
Daniel C. Sobral

6
@ unicornaddict - สิ่งที่ดีเกี่ยวกับวิธีนี้คือมันชัดเจนมากเมื่อคุณชี้ให้เห็นฉันเกลียดตัวเองที่ไม่ได้คิดถึงมัน!
James B

101

แน่นอนว่าคำตอบที่ดีกว่าคือ "ฉันจะถามชุมชน stackoverflow และอาจมีคำตอบที่ดีอย่างน้อย 4 คำตอบภายใน 5 นาที" สมองเป็นสิ่งที่ดี แต่ฉันต้องการคุณค่าที่สูงกว่าสำหรับคนที่รู้จักวิธีทำงานกับผู้อื่นเพื่อรับวิธีแก้ปัญหา


14
+1 สำหรับแก้มที่แท้จริง ทำวันของฉัน :-)
Azure แพลตตินัม

5
หากพวกเขาไม่เห็นด้วยคุณสามารถลิงก์ไปยังคำถามนี้ได้
แคม

51
การตีมือถือของคุณในระหว่างการสัมภาษณ์อาจถือได้ว่าเป็นการหยาบคายและท้ายที่สุดพวกเขาก็จ้างจอนสeet
tstenner

2
จริง ๆ แล้วอาจเป็นสิ่งที่ฉันจะพูด
Chris Dutrow

6
ฉันไม่คิดว่าพวกเขาจะสามารถซื้อ Jon Skeet ได้
SolutionYogi

49

ตัวอย่างไพ ธ อนอื่น (ตามคำตอบ):

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

1
น่าสนใจที่ฉันคิดว่าs2จะทำซ้ำมากกว่าs1ด้วย ... จากนั้นก็ตระหนักว่าความสัมพันธ์นั้นสมมาตรกันอยู่แล้ว
Matthieu M.

1
หากสตริงอาจยาวได้นี่เป็นเวอร์ชัน Python ที่ใช้ Boyer-Moore ในการรับ O (n): o is defation (s1, s2): return len (s1) == len (s2) และ re.compile (อีกครั้ง .escape (s1)). การค้นหา (2 * s2) ไม่ใช่ไม่มี
Duncan

2
@Duncan: ตัวinดำเนินการไม่ได้ใช้อัลกอริทึม O (n) หรือไม่
Ken Bloom

1
@Duncan: วิธีการของสตริงไพ ธ อนนั้นใช้ Boyer-Moore-Horspool ฉันสงสัยว่า Java มีการเพิ่มประสิทธิภาพที่คล้ายคลึงกันหรือไม่
โทมัส Ahle

1
@ ขอบคุณสำหรับการชี้ให้เห็นว่า ฉันคิดว่ามีเพียงนิพจน์ทั่วไปที่ใช้ Boyer-Moore แต่ฉันเห็นว่าฉันผิด สำหรับ Python 2.4 และก่อนหน้าคำตอบของฉันถูกต้อง แต่เนื่องจาก Python 2.5 ได้s1 in s2รับการปรับให้เหมาะสม ดูeffbot.org/zone/stringlib.htmสำหรับคำอธิบายของอัลกอริทึม Google ดูเหมือนจะระบุว่า Java ไม่มีการค้นหาสตริงอย่างรวดเร็ว (ดูตัวอย่างjohannburkard.de/software/stringsearch ) แม้ว่าฉันจะสงสัยว่ามันจะแตกอะไรถ้าเปลี่ยนไป
Duncan

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 - มันดูน่าสนใจมาก!
Martin Vseticka

25

แก้ไข: คำตอบที่ได้รับการยอมรับนั้นชัดเจนกว่าและมีประสิทธิภาพมากกว่านี้หากคุณมองเห็น ฉันออกจากคำตอบนี้เป็นสิ่งที่ฉันจะทำถ้าฉันไม่ได้คิดที่จะเพิ่มสายอักขระเดิมเป็นสองเท่า


ฉันแค่จะดุร้ายมัน ตรวจสอบความยาวก่อนจากนั้นลองชดเชยการหมุนที่เป็นไปได้ทุกครั้ง หากไม่มีใครทำงานออกกลับเท็จ - ถ้ามีคนใดทำกลับจริงทันที

ไม่จำเป็นต้องต่อกันเป็นพิเศษ - เพียงใช้พอยน์เตอร์ (C) หรือดัชนี (Java) และเดินไปมาทั้งคู่หนึ่งตัวในแต่ละสตริง - เริ่มต้นที่จุดเริ่มต้นของหนึ่งสตริงและการหมุนเวียนตัวเลือกผู้สมัครปัจจุบันในสตริงที่สอง . ตรวจสอบความเท่าเทียมกันของอักขระในแต่ละจุดในสตริง หากคุณไปยังจุดสิ้นสุดของสตริงแรกคุณก็เสร็จสิ้น

มันอาจจะเป็นเรื่องง่ายที่จะต่อกัน - แม้ว่าอาจจะมีประสิทธิภาพน้อยกว่า แต่อย่างน้อยใน Java


8
+1 - เราไม่ต้องการโซลูชันที่สวยงามที่ทำงานใน 3+ เท่าของโซลูชั่นที่มีประสิทธิภาพมากที่สุด นี้คือ C ... ไมโครเพิ่มประสิทธิภาพเด riguer
Stephen C

8
ผู้สัมภาษณ์: พูดคุย แต่ฉันคิดว่าผู้ชายคนนี้ไม่สามารถเขียนรหัสได้
Humphrey Bogart

8
@Beau: หากใครต้องการที่จะคิดว่าพวกเขายินดีที่จะขอรหัสฉัน ถ้ามีคนถามฉันว่า "ฉันจะทำอย่างไร" ฉันมักจะอธิบายอัลกอริทึมแทนที่จะกระโจนไปที่รหัส
Jon Skeet

3
@ จอน - ฉันอ่านความคิดเห็นของ Beau ว่าเป็นเรื่องตลก
oxbow_lakes

37
@ จอนมันเป็นเรื่องตลก! ผู้สัมภาษณ์ไม่สัมภาษณ์ Jon Skeet, Jon Skeet สัมภาษณ์เขา
Humphrey Bogart

17

นี่คือการใช้ regex เพียงเพื่อความสนุกสนาน:

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 สำหรับการเป็นมาสเตอร์ regex
Chris Thornton

-1 สำหรับการวางคำว่า "regex" และ "สนุก" ในคำสั่งเดียวกันโดยไม่ต้องแก้ไข "สนุก" กับ "ไม่" (ล้อเล่นเท่านั้นฉันไม่ลงคะแนน)
Binary Worrier

-3 หมายถึงว่า regexes ไม่สนุก
manlycode

ร่างกายใด ๆ สามารถอธิบายวิธีการ regex นี้ "(. *) (. *) = \\ 2 \\ 1" ทำงาน!
มาเฟีย

10

อ๊ะโอ้โห ... ทำไมทุกคนถึงตื่นเต้นกับO(n^2)คำตอบล่ะ? ฉันคิดว่าเราทำได้ดีกว่าที่นี่ คำตอบข้างต้นรวมถึงการO(n)ดำเนินการในO(n)วง (การเรียกสตริงย่อย / indexOf) แม้จะมีอัลกอริทึมการค้นหาที่มีประสิทธิภาพมากขึ้น พูดBoyer-MooreหรือKMPกรณีที่เลวร้ายที่สุดยังคงO(n^2)มีซ้ำ

O(n)คำตอบที่ตรงไปตรงมาแบบสุ่ม; ใช้แฮช (เช่นลายนิ้วมือ Rabin) ที่รองรับ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)แต่ยังคงมีค่าคงตัวที่ใหญ่กว่าอัลกอริธึม CT

ทั้งหมดที่กล่าวว่าฉันเป็นบวกอย่างแน่นอน 100% ว่ามีO(n)วิธีแก้ไขปัญหาที่กำหนดที่นี่ แต่ darned ถ้าฉันสามารถหาได้


KMP บนสตริงที่ต่อกันกับตัวเอง (ทั้งทางร่างกายหรือจริงด้วย%stringsize) รับประกันว่าจะเป็นเวลาเชิงเส้น
Kragen Javier Sitaker

+1 สำหรับ Rabin-Karp ซึ่งแตกต่างจาก KMP มันใช้พื้นที่คงที่และใช้ง่ายกว่า (มันเป็นคำตอบแรกที่ฉันคิดในไม่กี่วินาทีทำให้ยากที่จะเห็นคำตอบ 'ถูกต้อง' เพราะมันอยู่ตรงนั้นและหวาน) ความคิดของคุณที่ทำให้ฉันนึกถึงอัลกอริทึมของชอร์ - ฉันสงสัยว่า วิธีการแก้ปัญหาควอนตัม - แต่มันโง่ไปแล้วใช่ไหม?
Darius Bacon

1
RK ไม่ได้ให้โซลูชัน O (n) ที่กำหนดขึ้นได้และ KMP คือ O (n) ในอวกาศซึ่งอาจไม่เป็นที่ต้องการ ค้นหาสองทางหรือค้นหาสตริงย่อย SMOA ซึ่งมีทั้ง O (n) ในเวลาและ O (1) ในพื้นที่ โดยวิธีการที่ glibc strstr ใช้ Two Way แต่ถ้าคุณเชื่อมสตริงเพื่อใช้แทนการใช้% len คุณจะกลับไปที่ O (n) ในช่องว่าง :-)
R .. GitHub หยุดช่วย ICE

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!
Humphrey Bogart

11
+1 มันเขียนได้ดีมาก C. และเพื่อความเป็นธรรมคำถามนี้จะถูกแท็ก 'c'
Nick Moore

5
ในรหัสนี้คุณได้เดินสตริงอย่างน้อย 2 ถ้าไม่ใช่ 3 ครั้ง (ใน strlen และ strcmp) คุณสามารถบันทึกการตรวจสอบนี้ด้วยตนเองและคุณสามารถรักษาตรรกะนั้นไว้ในวง ในขณะที่คุณกำลังวนลูปถ้าการนับตัวอักษรหนึ่งสตริงแตกต่างจากตัวอื่นให้ออกจากลูป คุณจะรู้ถึงความยาวตามที่คุณรู้ว่าเป็นจุดเริ่มต้นและคุณจะรู้เมื่อคุณกดปุ่มสิ้นสุดของโมฆะ
Nasko

12
@Beau Martinez - เพราะบางครั้งเวลาดำเนินการมีความสำคัญมากกว่าเวลาในการพัฒนา :-)
phkahler

2
@phkahler - สิ่งที่มันอาจจะช้าลง ฟังก์ชันดัชนีในภาษาอื่น ๆ มักใช้อัลกอริธึมการค้นหาสตริงอย่างรวดเร็วเช่น Boyer-Moore, Rabin-Karp หรือ Knuth-Morris-Pratt มันไร้เดียงสาเกินไปที่จะสร้างสิ่งใหม่ใน C และคิดว่ามันเร็วกว่า
โทมัส Ahle

8

นี่คือO(n)และในสถานที่ alghoritm มันใช้<ประกอบการสำหรับองค์ประกอบของสตริง มันไม่ใช่ของฉันแน่นอน ฉันเอามันมาจากที่นี่ (เว็บไซต์นี้อยู่ในโปแลนด์ฉันสะดุดมันครั้งหนึ่งในอดีตและฉันไม่สามารถหาอะไรอย่างนั้นเป็นภาษาอังกฤษได้ในขณะนี้ดังนั้นฉันจึงแสดงสิ่งที่ฉันมี :))

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)เป็นเพียงแค่ดื่มด่ำมากจากมุมมองcomp-sciมากกว่าโซลูชันO (n) ที่ไม่ใช่:) :)
ไวยากรณ์ T3rr0r

4
+1 สำหรับโซลูชันที่เหมาะสมที่สุดในเวลาและใกล้เคียงที่สุดในขนาดรหัส (ทั้งไบนารีและ LoC) คำตอบนี้จะดียิ่งขึ้นด้วยคำอธิบาย
. GitHub หยุดช่วย ICE

งงงันอย่างเต็มที่ เราต้องการคำอธิบาย!
j_random_hacker

7

ฉันเดาว่าจะทำเช่นนี้ในJava:

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

ใน Perl ฉันจะทำ:

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

หรือดียิ่งขึ้นโดยใช้ฟังก์ชั่นดัชนีแทนการ regex:

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

1
คุณลืมใน\Q /\Q$string2/
Kragen Javier Sitaker

3
\Qคำพูดตัวอักษรพิเศษใด ๆ $string2ใน หากไม่มีมัน.จะถือว่าเป็นการหมุนของสตริง 1 อักขระใด ๆ
jackrabbit

6

ไม่แน่ใจว่านี้เป็นวิธีที่มีประสิทธิภาพมากที่สุด แต่ก็อาจจะมีความน่าสนใจ : ที่Burrows-Wheeler เปลี่ยน ตามบทความ WP การหมุนทั้งหมดของอินพุตให้เอาต์พุตเดียวกัน สำหรับแอปพลิเคชันเช่นการบีบอัดข้อมูลนี้ไม่พึงประสงค์ดังนั้นการหมุนดั้งเดิมจะถูกระบุ (เช่นโดยดัชนีดูที่บทความ) แต่สำหรับการเปรียบเทียบที่ไม่ขึ้นกับการหมุนอย่างง่ายมันฟังดูดี แน่นอนว่ามันไม่จำเป็นต้องมีประสิทธิภาพอย่างสมบูรณ์แบบ!


เนื่องจากการแปลง Burrows-Wheeler เกี่ยวข้องกับการคำนวณการหมุนทั้งหมดของสตริงจึงไม่เหมาะสมที่สุด .. :-)
R .. GitHub STOP กำลังช่วยเหลือ ICE

6

ใช้ตัวละครแต่ละตัวเป็นแอมพลิจูดและทำการแปลงฟูริเยร์โดยสิ้นเชิง หากพวกเขาแตกต่างกันเพียงการหมุนสเปกตรัมความถี่จะเหมือนกันกับภายในข้อผิดพลาดการปัดเศษ แน่นอนว่านี่ไม่มีประสิทธิภาพเว้นแต่ความยาวคือพลังของ 2 ดังนั้นคุณสามารถทำ FFT :-)


เราใช้สิ่งนี้เป็นแบบฝึกหัดการเข้ารหัสที่น่าสนใจฉันไม่แน่ใจว่าเราจะสามารถประเมินสิ่งนั้นได้)
jayshao

การละเมิด FFT :) +1 จากฉัน
Aamir

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

[แก้ไข: 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

ขอบคุณที่สังเกตเห็นข้อบกพร่อง แก้ไขตอนนี้
Michael Buen

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
Tony Delroy

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 () เป็นแพลตฟอร์มเฉพาะ แต่หากโดยเฉพาะอย่างยิ่งสมองตายสามารถถูกแทนที่ด้วยทางเลือกที่เร็วกว่าเช่นอัลกอริทึม Boyer-Moore)


1
คุณรู้จักแพลตฟอร์มที่มีstrstr()ใน O (n + m) หรือไม่? นอกจากนี้หากมาตรฐาน (หรืออย่างอื่น) ไม่รับประกันว่าคุณจะใช้เวลาในการดำเนินการเชิงเส้นstrstr()คุณไม่สามารถยืนยันได้ว่าอัลกอริธึมทั้งหมดมีความสอดคล้องของเวลาเชิงเส้น
jpalecek

นี่คือเหตุผลที่ฉันบอกว่ามันสามารถถูกแทนที่ด้วยอัลกอริทึม Boyer-Moore ทำให้มันทำงานในเวลาเชิงเส้น
RarrRarrRarr

มีปัญหาที่อาจเกิดขึ้นสองสามวิธีในการจัดสรรของคุณs1SelfConcat: เนื่องจาก C9x ที่ C อนุญาตขนาดอาร์เรย์ที่หลากหลาย (แม้ว่า GCC อนุญาตให้ใช้นานกว่านั้น) และคุณจะพบปัญหาในการจัดสรรสตริงขนาดใหญ่บนสแต็ก Yosef Kreinin เขียนบล็อกโพสต์ที่น่าขบขันอย่างมากเกี่ยวกับปัญหานี้ นอกจากนี้วิธีการแก้ปัญหาของคุณยังคงเป็นกำลังสองกับ Boyer-Moore; คุณต้องการ KMP
Kragen Javier Sitaker


2

ฉันชอบคำตอบที่ตรวจสอบว่า s2 เป็นซับสตริงของ s1 ต่อกับ s1

ฉันต้องการเพิ่มประสิทธิภาพที่ไม่ทำให้เสียความสง่างาม

แทนที่จะเชื่อมโยงสตริงคุณสามารถใช้มุมมองเข้าร่วม (ฉันไม่รู้ภาษาอื่น แต่สำหรับ C ++ Boost.Range ให้มุมมองแบบนั้น)

เนื่องจากการตรวจสอบว่าสตริงเป็นสตริงย่อยของอีกสตริงหนึ่งมีความซับซ้อนเฉลี่ยเชิงเส้น (ความซับซ้อนของกรณีที่แย่ที่สุดคือกำลังสอง) การเพิ่มประสิทธิภาพนี้ควรปรับปรุงความเร็วโดยเฉลี่ยเป็น 2 เท่า


2

คำตอบของจาวาบริสุทธิ์ (ตรวจสอบเป็นโมฆะ sans)

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

และตอนนี้สำหรับสิ่งที่แตกต่างอย่างสิ้นเชิง

หากคุณต้องการคำตอบที่รวดเร็วจริงๆในบริบทที่มีข้อ จำกัด บางอย่างเมื่อสตริงไม่ได้หมุนกัน

  • คำนวณการตรวจสอบโดยใช้อักขระบางตัว (เช่น xoring อักขระทั้งหมด) บนทั้งสองสตริง หากลายเซ็นแตกต่างกันสตริงจะไม่หมุนกัน

ตกลงมันสามารถล้มเหลว แต่มันเร็วมากที่จะบอกว่าถ้าสายไม่ตรงกันและถ้าพวกเขาตรงกับที่คุณยังคงสามารถใช้อัลกอริทึมอื่นเช่นการเรียงต่อสตริงเพื่อตรวจสอบ



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
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เวลาจะน้อยกว่า

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.