นับจำนวนคำที่เป็นวงกลมในอินพุต


9

คำรอบ

คำชี้แจงปัญหา

เรานึกถึงคำที่เป็นวงจรเป็นคำที่เขียนในวงกลม เพื่อเป็นตัวแทนของคำวงจรเราเลือกตำแหน่งเริ่มต้นโดยพลการและอ่านตัวอักษรตามลำดับตามเข็มนาฬิกา ดังนั้น "รูปภาพ" และ "turepic" จึงเป็นตัวแทนของคำวงจรเดียวกัน

คุณจะได้รับสตริง [] คำแต่ละองค์ประกอบซึ่งเป็นตัวแทนของคำวงจร ส่งคืนจำนวนคำวัฏจักรที่ต่างกันที่แสดง

ชนะเร็วที่สุด (Big O โดยที่ n = จำนวนตัวอักษรในสตริง)


3
หากคุณกำลังมองหาคำวิจารณ์รหัสของคุณสถานที่ที่ควรไปคือ codereview.stackexchange.com
Peter Taylor

เย็น. ฉันจะแก้ไขเพื่อเน้นความท้าทายและย้ายบทวิจารณ์ไปยังบทวิจารณ์โค้ด ขอบคุณปีเตอร์
eggonlegs

1
เกณฑ์การชนะคืออะไร รหัสสั้นที่สุด (Code Golf) หรืออะไรอย่างอื่น? มีข้อ จำกัด ใด ๆ ในรูปแบบของอินพุตและเอาต์พุตหรือไม่ เราจำเป็นต้องเขียนฟังก์ชั่นหรือโปรแกรมที่สมบูรณ์หรือไม่? มันต้องเป็นภาษาจาวาหรือเปล่า?
ugoren

1
@eggonlegs คุณระบุ big-O - แต่เกี่ยวกับพารามิเตอร์ใด จำนวนสตริงในอาร์เรย์? การเปรียบเทียบสตริงนั้นเป็น O (1) หรือไม่ หรือจำนวนตัวอักษรในสตริงหรือจำนวนตัวอักษร? หรือสิ่งอื่นใด
Howard

1
@ เพื่อนแน่นอนมันเป็น 4?
ปีเตอร์เทย์เลอร์

คำตอบ:


4

หลาม

นี่คือทางออกของฉัน ฉันคิดว่ามันอาจจะยังคงเป็น O (n 2 ) แต่ฉันคิดว่าคดีโดยเฉลี่ยดีกว่านั้นมาก

โดยทั่วไปจะทำงานโดย normalizing แต่ละสายเพื่อให้การหมุนใด ๆ จะมีรูปแบบเดียวกัน ตัวอย่างเช่น:

'amazing' -> 'mazinga'
'mazinga' -> 'mazinga'
'azingam' -> 'mazinga'
'zingama' -> 'mazinga'
'ingamaz' -> 'mazinga'
'ngamazi' -> 'mazinga'
'gamazin' -> 'mazinga'

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

ฟื้นฟูเป็น n 2ในกรณีที่เลวร้ายที่สุด (ที่ตัวละครในสตริงทุกคนมีเหมือนกันเช่นaaaaaa) nแต่ส่วนใหญ่เวลามีเพียงจะเป็นเหตุการณ์ที่เกิดขึ้นไม่กี่และเวลาทำงานจะได้ใกล้ชิดกับ

บนแล็ปท็อปของฉัน (ดูอัลคอร์ Intel Atom @ 1.66GHz และ RAM 1GB) ทำงานบน/usr/share/dict/words(234,937 คำที่มีความยาวเฉลี่ย 9.5 ตัวอักษร) ใช้เวลาประมาณ 7.6 วินาที

#!/usr/bin/python

import sys

def normalize(string):
   # the minimum character in the string
   c = min(string) # O(n) operation
   indices = [] # here we will store all the indices where c occurs
   i = -1       # initialize the search index
   while True: # finding all indexes where c occurs is again O(n)
      i = string.find(c, i+1)
      if i == -1:
         break
      else:
         indices.append(i)
   if len(indices) == 1: # if it only occurs once, then we're done
      i = indices[0]
      return string[i:] + string[:i]
   else:
      i = map(lambda x:(x,x), indices)
      for _ in range(len(string)):                       # go over the whole string O(n)
         i = map(lambda x:((x[0]+1)%len(string), x[1]), i)  # increment the indexes that walk along  O(m)
         c = min(map(lambda x: string[x[0]], i))    # get min character from current indexes         O(m)
         i = filter(lambda x: string[x[0]] == c, i) # keep only the indexes that have that character O(m)
         # if there's only one index left after filtering, we're done
         if len(i) == 1:
            break
      # either there are multiple identical runs, or
      # we found the unique best run, in either case, we start the string from that
      # index
      i = i[0][0]
      return string[i:] + string[:i]

def main(filename):
   cyclic_words = set()
   with open(filename) as words:
      for word in words.readlines():
         cyclic_words.add(normalize(word[:-1])) # normalize without the trailing newline
   print len(cyclic_words)

if __name__ == '__main__':
   if len(sys.argv) > 1:
      main(sys.argv[1])
   else:
      main("/dev/stdin")

3

Python (3) อีกครั้ง

วิธีที่ฉันใช้คือการคำนวณแฮชการหมุนของแต่ละคำเริ่มต้นที่แต่ละอักขระในสตริง เนื่องจากเป็นแฮชการกลิ้งจึงใช้เวลา O (n) (โดยที่ n คือความยาวของคำ) ในการคำนวณแฮชทั้งหมด สตริงจะถือว่าเป็นหมายเลขฐาน 1114112 ซึ่งทำให้มั่นใจได้ว่าแฮชจะไม่ซ้ำกัน (ซึ่งคล้ายกับโซลูชัน Haskell แต่มีประสิทธิภาพมากกว่าเนื่องจากจะผ่านสายอักขระสองครั้งเท่านั้น)

จากนั้นสำหรับแต่ละคำที่ป้อนเข้าอัลกอริทึมจะตรวจสอบแฮชต่ำสุดเพื่อดูว่ามันอยู่ในชุดของแฮชที่เห็นอยู่หรือไม่ (ชุด Python ดังนั้นการค้นหาคือ O (1) ในขนาดของชุด); ถ้าเป็นเช่นนั้นคำหรือการหมุนใดคำหนึ่งของมันจะถูกเห็น มิฉะนั้นจะเพิ่มแฮชนั้นเข้าไปในชุด

อาร์กิวเมนต์บรรทัดคำสั่งควรเป็นชื่อของไฟล์ที่มีหนึ่งคำต่อบรรทัด (เช่น/usr/share/dict/words)

import sys

def rollinghashes(string):
    base = 1114112
    curhash = 0
    for c in string:
        curhash = curhash * base + ord(c)
    yield curhash
    top = base ** len(string)
    for i in range(len(string) - 1):
        curhash = curhash * base % top + ord(string[i])
        yield curhash

def cycles(words, keepuniques=False):
    hashes = set()
    uniques = set()
    n = 0
    for word in words:
        h = min(rollinghashes(word))
        if h in hashes:
            continue
        else:
            n += 1
            if keepuniques:
                uniques.add(word)
            hashes.add(h)
    return n, uniques

if __name__ == "__main__":
    with open(sys.argv[1]) as words_file:
        print(cycles(line.strip() for line in words_file)[0])

1

Haskell

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

ถ้า n คือจำนวนของคำในรายการและ m เป็นความยาวของคำแล้วคำนวณ 'จำนวนวงจรกลุ่ม' สำหรับทุกคำที่มีการO(n*m)เรียงลำดับและการจัดกลุ่มO(n log n)O(n)

import Data.List
import Data.Char
import Data.Ord
import Data.Function

groupUnsortedOn f = groupBy ((==) `on` f) . sortBy(compare `on` f)
allCycles w = init $ zipWith (++) (tails w)(inits w)
wordval = foldl (\a b -> a*256 + (fromIntegral $ ord b)) 0
uniqcycle = minimumBy (comparing wordval) . allCycles
cyclicGroupCount = length . groupUnsortedOn uniqcycle

1

มาติกา

ตัดสินใจที่จะเริ่มอีกครั้งตอนนี้ฉันเข้าใจกฎของเกม (ฉันคิดว่า)

พจนานุกรมคำศัพท์ 10,000 คำที่ประกอบด้วย "คำว่า" (เฉพาะตัวพิมพ์เล็ก) ที่มีความยาวไม่เกิน 3 ตัวในลักษณะเดียวกันพจนานุกรมอื่น ๆ ที่ถูกสร้างขึ้นประกอบด้วยสตริงความยาว 4, 5, 6, 7 และ 8

ClearAll[dictionary]      
dictionary[chars_,nWords_]:=DeleteDuplicates[Table[FromCharacterCode@RandomInteger[{97,122},
chars],{nWords}]];
n=16000;
d3=Take[dictionary[3,n],10^4];
d4=Take[dictionary[4,n],10^4];
d5=Take[dictionary[5,n],10^4];
d6=Take[dictionary[6,n],10^4];
d7=Take[dictionary[7,n],10^4];
d8=Take[dictionary[8,n],10^4];

gใช้พจนานุกรมเวอร์ชันปัจจุบันเพื่อตรวจสอบ คำบนสุดจะถูกรวมเข้ากับตัวแปรหลากหลาย (ถ้ามี) คำและคำที่ตรงกันจะถูกผนวกเข้ากับรายการผลลัพธ์outของคำที่ประมวลผล คำที่ส่งออกจะถูกลบออกจากพจนานุกรม

g[{wds_,out_}] := 
   If[wds=={},{wds,out},
   Module[{s=wds[[1]],t,c},
   t=Table[StringRotateLeft[s, k], {k, StringLength[s]}];
   c=Intersection[wds,t];
   {Complement[wds,t],Append[out,c]}]]

f วิ่งผ่านพจนานุกรมคำศัพท์ทุกคำ

f[dict_]:=FixedPoint[g,{dict,{}}][[2]]

ตัวอย่างที่ 1 : คำจริง

r = f[{"teaks", "words", "spot", "pots", "sword", "steak", "hand"}]
Length[r]

{{"สเต็ก", "teaks"}, {"hand"}, {"pots", "spot"}, {"ดาบ", "คำ"}}
4


ตัวอย่างที่ 2 : คำประดิษฐ์ พจนานุกรมสตริงที่มีความยาว 3 จากนั้นจำนวนคำวัฏจักร

f[d3]//AbsoluteTiming
Length[%[[2]]]

d3

5402


การจับเวลาเป็นฟังก์ชั่นความยาวของคำ 10,000 คำในแต่ละพจนานุกรม

การกำหนดเวลา

ฉันไม่รู้วิธีตีความผลการวิจัยในแง่ของ O โดยเฉพาะอย่างยิ่งระยะเวลาประมาณสองเท่าจากพจนานุกรมตัวละครสามตัวถึงพจนานุกรมตัวละครสี่ตัว ระยะเวลาเพิ่มขึ้นเกือบประมาทเลินเล่อจาก 4 ถึง 8 ตัวอักษร


คุณสามารถโพสต์ลิงก์ไปยังพจนานุกรมที่คุณใช้เพื่อที่ฉันจะได้เปรียบเทียบกับของคุณได้หรือไม่?
eggonlegs

ลิงก์ต่อไปนี้ใน dictionary.txt ควรใช้งานได้: bitshare.com/files/oy62qgro/dictionary.txt.html (ขออภัยเกี่ยวกับนาทีที่คุณจะต้องรอให้การดาวน์โหลดเริ่มต้น) BTW ไฟล์มี 3char, 4char ... พจนานุกรม 8char ทั้งหมดรวมกัน 10,000 คำในแต่ละคำ คุณจะต้องการแยกพวกเขา
DavidC

น่ากลัว ขอบคุณมาก :)
eggonlegs

1

สิ่งนี้สามารถทำได้ใน O (n) หลีกเลี่ยงเวลากำลังสอง ความคิดคือการสร้างวงกลมเต็มภายในสตริงฐานสองครั้ง ดังนั้นเราจึงสร้าง "amazingamazin" เป็นสตริงวงกลมเต็มรูปแบบเพื่อตรวจสอบสตริง cyclic ทั้งหมดที่ตรงกับ "amazing"

ด้านล่างเป็นโซลูชัน Java:

public static void main(String[] args){
    //args[0] is the base string and following strings are assumed to be
    //cyclic strings to check 
    int arrLen = args.length;
    int cyclicWordCount = 0;
    if(arrLen<1){
        System.out.println("Invalid usage. Supply argument strings...");
        return;
    }else if(arrLen==1){
        System.out.println("Cyclic word count=0");
        return;         
    }//if

    String baseString = args[0];
    StringBuilder sb = new StringBuilder();
    // Traverse base string twice appending characters
    // Eg: construct 'amazingamazin' from 'amazing'
    for(int i=0;i<2*baseString.length()-1;i++)
        sb.append(args[0].charAt(i%baseString.length()));

    // All cyclic strings are now in the 'full circle' string
    String fullCircle = sb.toString();
    System.out.println("Constructed string= "+fullCircle);

    for(int i=1;i<arrLen;i++)
    //Do a length check in addition to contains
     if(baseString.length()==args[i].length()&&fullCircle.contains(args[i])){
        System.out.println("Found cyclic word: "+args[i]);
        cyclicWordCount++;
    }

    System.out.println("Cyclic word count= "+cyclicWordCount);
}//main

0

ฉันไม่รู้ว่านี่มีประสิทธิภาพมากหรือไม่ แต่นี่เป็นรอยแตกแรกของฉัน

private static int countCyclicWords(String[] input) {
    HashSet<String> hashSet = new HashSet<String>();
    String permutation;
    int count = 0;

    for (String s : input) {
        if (hashSet.contains(s)) {
            continue;
        } else {
            count++;
            for (int i = 0; i < s.length(); i++) {
                permutation = s.substring(1) + s.substring(0, 1);
                s = permutation;
                hashSet.add(s);
            }
        }
    }

    return count;
}

0

Perl

ไม่แน่ใจว่าฉันเข้าใจปัญหา แต่ตรงกับตัวอย่าง @dude ที่โพสต์ในความคิดเห็นอย่างน้อย โปรดแก้ไขการวิเคราะห์ที่ไม่ถูกต้องอย่างแน่นอน

สำหรับแต่ละคำ W ใน N คำที่กำหนดของรายการสตริงคุณต้องก้าวผ่านอักขระทั้งหมดของ W ในกรณีที่เลวร้ายที่สุด ฉันต้องถือว่าการดำเนินการแฮชเสร็จในเวลาคงที่

use strict;
use warnings;

my @words = ( "teaks", "words", "spot", "pots", "sword", "steak", "hand" );

sub count
{
  my %h = ();

  foreach my $w (@_)
  {
    my $n = length($w);

    # concatenate the word with itself. then all substrings the
    # same length as word are rotations of word.
    my $s = $w . $w;

    # examine each rotation of word. add word to the hash if
    # no rotation already exists in the hash
    $h{$w} = undef unless
      grep { exists $h{substr $s, $_, $n} } 0 .. $n - 1;
  }

  return keys %h;
}

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