Temukan karakter aneh dalam sebuah pola

20

Memasukkan

Baris pertama adalah string tertentu yang diulang beberapa kali. Misalnya, bisa jadi abcabcabcabc, [];[];[];, dll dapat dipotong; misalnya: 1231231231. Selalu temukan string terpendek; misalnya, jika garisnya adalah 22222, maka stringnya adalah 2, bukan 22atau 22222atau apa pun. String akan selalu diulang setidaknya 2 kali penuh.

Semua baris berikutnya akan menjadi pola yang diimbangi dengan angka apa pun. Misalnya, bisa jadi:

abcabcabc
cabcabcab
bcabcabca

(diimbangi dengan 1), atau bisa juga:

abcdefabcdefabcdefabc
cdefabcdefabcdefabcde
efabcdefabcdefabcdefa

(diimbangi dengan 4).

Salah satu karakter dalam input akan salah. (Dijamin tidak berada di baris pertama.) Misalnya, dalam input ini:

a=1a=1a=1
=1a=1a=1a
1a=11=1a=
a=1a=1a=1
=1a=1a=1a

yang 1pada baris 3 adalah aneh keluar.

Keluaran

Anda harus mengeluarkan koordinat (berbasis nol, mulai dari kiri atas) dari yang aneh. Misalnya, dalam input di atas, output yang sesuai adalah 4,2. Anda juga dapat menampilkan 4 2, atau "4""2", atau bahkan [[4],[2]], atau format lainnya, selama Anda dapat mengetahui seperti apa seharusnya hasil tersebut.

Uji kasus

Memasukkan:

codegolfcodegolfco
egolfcodegolfcodeg
lfcodegolfcodegoff
odegolfcodegolfcod
golfcodegolfcodego
fcodegolfcodegolfc

Keluaran: 16,2

Memasukkan:

][[][][[][][[][][[][][[
[][][[][][[][][[][][[][
[][[][][[][][[][][[][][
[[][][[]]][[][][[][][[]

Keluaran: 8,3

Memasukkan:

...
. .
...

Keluaran: 1,1

Memasukkan:

ababa
babab
ababb
babab

Keluaran: 4,2

Pergi!

Gagang pintu
sumber
Karakter apa yang bisa dimasukkan dalam string? Cetak ASCII? ASCII? Unicode?
Dennis
@ Dennis Hanya ASCII yang dapat dicetak (yang pada dasarnya dapat diasumsikan untuk setiap tantangan yang melibatkan string; jika tidak kita harus menentukan bahwa untuk hampir setiap tantangan: P)
Doorknob
Saya rasa begitu. Saya berpikir tentang pendekatan yang membutuhkan arang yang tidak digunakan, jadi saya pikir saya akan bertanya.
Dennis
Haruskah kita memeriksa case seperti ini: abc/cab/abc- dan output di 0 2sini?
user2846289
@VadimR Tidak, karena hanya satu karakter yang akan salah.
Gagang Pintu

Jawaban:

7

Bash Perl, 231 229 218 178 164 166 138 106 74 byte

/^(((.*).*)\2+)\3$/;$_.=$1x2;$.--,die$+[1]if/^(.*)(.)(.*)
.*\1(?!\2).\3/

Script mengharuskan menggunakan -nswitch, yang menyumbang dua byte.

Gagasan menambahkan dua salinan dari semua pengulangan penuh dari pola telah diambil dari jawaban MT0 .

Berbeda dengan semua jawaban lain, pendekatan ini mencoba mengekstraksi pola jalur input saat ini di setiap iterasi; itu akan gagal pada baris yang berisi karakter aneh (dan menggunakan pola baris sebelumnya sebagai gantinya). Ini dilakukan untuk memasukkan ekstraksi pola dalam loop, yang berhasil menghemat beberapa byte.

Versi tidak disatukan

#!/usr/bin/perl -n

# The `-n' switch makes Perl execute the entire script once for each input line, just like
# wrapping `while(<>){…}' around the script would do.

/^(((.*).*)\2+)\3$/;

# This regular expression matches if `((.*).*)' - accessible via the backreference `\2' -
# is repeated at least once, followed by a single repetition of `\3" - zero or more of the
# leftmost characters of `\2' - followed by the end of line. This means `\1' will contain
# all full repetitions of the pattern. Even in the next loop, the contents of `\1' will be
# available in the variable `$1'.

$_.=$1x2;

# Append two copies of `$1' to the current line. For the line, containing the odd
# character, the regular expression will not have matched and the pattern of the previous
# line will get appended.
#
# Since the pattern is repeated at least two full times, the partial pattern repetition at
# the end of the previous line will be shorter than the string before it. This means that
# the entire line will the shorter than 1.5 times the full repetitions of the pattern, 
# making the two copies of the full repetitions of the pattern at least three times as 
# long as the input lines.

$.-- , die $+[1] if

# If the regular expression below matches, do the following:
#
#   1. Decrement the variable `$.', which contains the input line number.
#
#      This is done to obtain zero-based coordinates.
#
#   2. Print `$+[1]' - the position of the last character of the first subpattern of the
#      regular expression - plus some additional information to STDERR and exit.
#
#      Notably, `die' prints the (decremented) current line number.

/^(.*)(.)(.*)
.*\1(?!\2).\3/;

# `(.*)(.)(.*)', enclosed by `^' and a newline, divides the current input line into three
# parts, which will be accesible via the backreferences `\1' to `\3'. Note that `\2'
# contains a single character.
#
# `.*\1(?!\2).\3' matches the current input line, except for the single character between
# `\1' and `\3' which has to be different from that in `\2', at any position of the line
# containing the pattern repetitions. Since this line is at least thrice as long as
# `\1(?!\2).\3', it will be matched regardless of by how many characters the line has been
# rotated.

Contoh

Untuk test case

codegolfcodegolfco
egolfcodegolfcodeg
lfcodegolfcodegoff
odegolfcodegolfcod
golfcodegolfcodego
fcodegolfcodegolfc

output dari versi golf adalah

16 at script.pl line 1, <> line 2.

artinya karakter aneh memiliki koordinat 16,2.

Pelanggaran terang-terangan ini mengambil keuntungan dari format keluaran liberal.

Tepat sebelum keluar, isi dari beberapa variabel khusus Perl adalah:

$_  = lfcodegolfcodegoff\ncodegolfcodegolfcodegolfcodegolf
$1  = lfcodegolfcodego
$2  = f
$3  = f

( $nberisi kecocokan dari subpola yang dapat diakses melalui backreference \n.)

Dennis
sumber
Penangkapan pintar dari unit balasan. Ini dapat dioptimalkan dengan satu byte:^((.*?)(.*?))(?=\1+\2$)
Heiko Oberdiek
Saya beralih ke bahasa yang digunakan anak-anak populer. Mungkin bisa bermain golf lebih jauh; ini adalah skrip Perl pertama saya dalam lebih dari satu dekade ...
Dennis
2
... dan Anda terlambat satu dekade jika menurut Anda perl adalah apa yang digunakan anak-anak populer
ardnew
jawaban ini tidak mendapatkan cinta yang layak. Sepertinya saya pemenang @Doorknob
ardnew
8

Perl, 212 191 181 168 bytes

$_=<>;/^(((.*?)(.*?))\2+)\3$/;$x=$1x4;while(<>){chop;$x=~/\Q$_\E/&&next;for$i(0..y///c-1){for$r(split//,$x){$b=$_;$b=~s/(.{$i})./$1$r/;$x=~/\Q$b\E/&&die$i,$",$.-1,$/}}}
  • Versi ini menggunakan trik yang dioptimalkan untuk menangkap unit balasan, dipelajari dalam jawaban Dennis .
  • Optimasi dengan menggunakan properti yang semua garis memiliki panjang yang sama.
  • Baris akhir juga diperlukan untuk baris terakhir, jika tidak, chompbukannya chopharus digunakan.
  • Pengoptimalan komentar ardnew ditambahkan.

Versi lama, 212 byte:

$_=<>;chop;/^(.+?)\1+(??{".{0,".(-1+length$1).'}'})$/;$;=$1;while(<>){$x=$;x length;chop;$x=~/\Q$_\E/&&next;for$i(0..-1+length$_){for$r(split//,$;){$b=$_;$b=~s/(.{$i})./$1$r/;$x=~/\Q$b\E/&&exit print$i,$",$.-1}}}

Versi tidak disatukan:

$_ = <>;  # read first line
/^(((.*?)(.*?))\2+)\3$/;
# The repeat unit \2 consists of \3 and \4,
# and the start part \2 can be added at the end (as partial or even full unit).
$x = $1 x 4; # $x is long enough to cover each following line

# Old version:
# /^(.+?)\1+(??{ ".{0," . (-1 + length $1) . '}' })$/;
# $a = $1; # $a is the repeat unit.
# The unit is caught by a non-greedy pattern (.+?) that is
# repeated at least once: \1+
# The remaining characters must be less than the unit length.
# The unit length is known at run-time, therefore a "postponed"
# regular expression is used for the remainder.

# process the following lines until the error is found
while (<>) {
    # old version:
    # $x = $a x length;
    # $x contains the repeated string unit, by at least one unit longer
    # than the string in the current line
    chop; # remove line end of current line
    $x =~ /\Q$_\E/ && next;
          # go to next line, if current string is a substring of the repeated units;
          # \Q...\E prevents the interpretation of special characters
    # now each string position $x is checked, if it contains the wrong character:
    for $i (0 .. y///c - 1) {  # y///c yields the length of $_
        for $r (split //, $x) { #/ (old version uses $a)
            # replace the character at position $i with a
            # character from the repeat unit
            $b = $_;
            $b =~ s/(.{$i})./$1$r/;
            $x =~ /\Q$b\E/
               && die $i, $", $. - 1, $/;
               # $" sets a space and the newline is added by $/;
               # the newline prevents "die" from outputting line numbers
        }
    }
}
Heiko Oberdiek
sumber
Solusi dan komentar yang bagus, saya perlu belajar lebih banyak regex;)
Newbrict
1
yang pertama choptidak perlu- harus dihapus. final exit printdapat diganti dengan die(tambahkan ,$/untuk menyembunyikan hal-hal tambahan (jika diperlukan)). juga length$_dapat diganti dengany///c
ardnew
@ardnew: Terima kasih banyak, saya telah menghapus yang pertama chop, karena $cocok sebelum baris baru di akhir string. Menyembunyikan hal-hal tambahan diemelalui baris baru yang ditambahkan tampaknya perlu bagi saya. Juga y///cjauh lebih pendek dari length$_dan satu byte lebih pendek daripada lengthtanpa yang tidak perlu $_.
Heiko Oberdiek
1
@ardnew: Saya lupa tentang kata kerja die . Bahkan termasuk mencetak nomor baris! Saya akan menggunakannya dalam pembaruan berikutnya.
Dennis
3

C, 187 byte

Keterbatasan.

  • Jangan gunakan string input yang lebih panjang dari 98 karakter :)

Versi golf

char s[99],z[99],*k,p,i,I,o,a;c(){for(i=0;k[i]==s[(i+o)%p];i++);return k[i];}main(){for(gets(k=s);c(p++););for(;!(o=o>p&&printf("%d,%d\n",I,a))&&gets(k=z);a++)while(o++<p&&c())I=I<i?i:I;}

Versi tidak disatukan

char s[99],z[99],*k,p,i,I,o,a;

c()
{
    for(i=0
       ;k[i]==s[(i+o)%p]
       ;i++)
       ;
    return k[i];
}

main()
{
    for(gets(k=s);c(p++);)
         ;
    for(;!(o=o>p&&printf("%d,%d\n",I,a)) && gets(k=z);a++)
           while(o++ < p && c())
            I=I<i?i:I;
}
asr
sumber
2

Python, 303 292

r=raw_input
R=range
s=r()
l=len(s)
m=1
g=s[:[all((lambda x:x[1:]==x[:-1])(s[n::k])for n in R(k))for k in R(1,l)].index(True)+1]*l*2
while 1:
 t=r()
 z=[map(lambda p:p[0]==p[1],zip(t,g[n:l+n]))for n in R(l)]
 any(all(y)for y in z)or exit("%d,%d"%(max(map(lambda b:b.index(False),z)),m))
 m+=1

Input melewati stdin. Saya akan menjelaskannya jika ada permintaan, tapi sepertinya saya tidak akan menang.

Fraxtil
sumber
1

Perl, 157 154

Sunting : -3 terima kasih atas saran ardnew.

<>=~/^(((.*?).*?)\2+)\3$/;$p=$2;$n=$+[1];while(<>){s/.{$n}/$&$&/;/(\Q$p\E)+/g;$s=$p;1while/./g*$s=~/\G\Q$&/g;print$n>--($m=pos)?$m:$m-$n,$",$.-1,$/if pos}

Butuh beberapa waktu (hidup dan mati, tentu saja, bukan 5 hari ;-)), dan gagasan tentang algoritma pada awalnya sulit dipahami (meskipun saya merasa itu ada di sana), tetapi akhirnya (dan tiba-tiba) semuanya menjadi jelas.

Jika panjang string adalah kelipatan dari panjang pola, dan bahkan jika string tidak dimulai dengan awal dari pola, string yang digabungkan dengan dirinya sendiri akan menghasilkan pola di tempat penggabungan (bayangkan pengulangan tak terbatas kata pada pita melingkar - tempat dari pengelasan tidak penting). Jadi, idenya adalah untuk memotong garis ke beberapa satuan panjang dan menyatukan aslinya. Hasilnya, bahkan untuk string yang berisi karakter yang salah, dijamin cocok dengan pola setidaknya sekali. Dari sana mudah untuk menemukan posisi karakter yang menyinggung.

Baris pertama dipinjam tanpa malu-malu dari jawaban Heiko Oberdiek :-)

<>=~/^(((.*?).*?)\2+)\3$/;      # Read first line, find the repeating unit
$p=$2;                          # and length of whole number of units.
$n=$+[1];                       # Store as $p and $n.
while(<>){                      # Repeat for each line.
    s/.{$n}/$&$&/;              # Extract first $n chars and
                                # append original line to them.
    /(\Q$p\E)+/g;               # Match until failure (not necessarily from the
                                # beginning - doesn't matter).
    $s=$p;                      # This is just to reset global match position
                                # for $s (which is $p) - we could do without $s,
                                # $p.=''; but it's one char longer.
                                # From here, whole pattern doesn't match -
    1while/./g*$s=~/\G\Q$&/g;   # check by single char.
                                # Extract next char (if possible), match to 
                                # appropriate position in a pattern (position 
                                # maintained by \G assertion and g modifier).
                                # We either exhaust the string (then pos is 
                                # undefined and this was not the string we're
                                # looking for) or find offending char position.

    print$n>--($m=pos)?$m:$m-$n,$",$.-1,$/if pos
}
pengguna2846289
sumber
1
kerja bagus. saya pikir Anda dapat mengganti /.{$n}/;$_=$&.$_;dengans/.{$n}/$&$&/;
ardnew
1

JavaScript (ES6) - 147 133 136 Karakter

s.split('\n').map((x,i)=>(v=/^(.*)(.)(.*)᛫.*\1(?!\2).\3/.exec(x+'᛫'+(a=/^(((.*).*)\2+)\3\n/.exec(s)[1])+a))&&console.log(v[1].length,i))

Mengharapkan string yang akan diuji dalam variabel sdan output hasilnya ke konsol.

var repetitionRE = /^(((.*).*)\2+)\3\n/;
                                        // Regular expression to find repeating sequence
                                        // without any trailing sub-string of the sequence.
var sequence = repetitionRE.exec(s)[1]; // Find the sequence string.
s.split('\n')                           // Split the input into an array.
 .map(
   ( row, index ) =>                    // Anonymous function using ES6 arrow syntax
   {
     var testStr = row + '᛫'+ sequence + sequence;
                                        // Concatenate the current row, a character which won't
                                        // appear in the input and two copies of the repetitions
                                        // of the sequence from the first line.
     var match = /^(.*)(.)(.*)᛫.*\1(?!\2).\3/.exec(testStr);
                                        // Left of the ᛫ finds sub-matches for a single
                                        // character and the sub-strings before and after.
                                        // Right of the ᛫ looks for any number of characters
                                        // then the before and after sub-matches with a
                                        // different character between.
      if ( match )
       console.log( match[1].length, index );
                                        // Output the index of the non-matching character
                                        // and the row.
   }         
 );

Test Case 1

s="codegolfcodegolfco\negolfcodegolfcodeg\nlfcodegolfcodegoff\nodegolfcodegolfcod\ngolfcodegolfcodego\nfcodegolfcodegolfc"
s.split('\n').map((x,i)=>(v=/^(.*)(.)(.*)᛫.*\1(?!\2).\3/.exec(x+'᛫'+(a=/^(((.*).*)\2+)\3\n/.exec(s)[1])+a))&&console.log(v[1].length,i))

Keluaran

16 2

Test Case 2

s="][[][][[][][[][][[][][[\n[][][[][][[][][[][][[][\n[][[][][[][][[][][[][][\n[[][][[]]][[][][[][][[]"
s.split('\n').map((x,i)=>(v=/^(.*)(.)(.*)᛫.*\1(?!\2).\3/.exec(x+'᛫'+(a=/^(((.*).*)\2+)\3\n/.exec(s)[1])+a))&&console.log(v[1].length,i))

Keluaran

8 3

Test Case 3

s="...\n. .\n..."
s.split('\n').map((x,i)=>(v=/^(.*)(.)(.*)᛫.*\1(?!\2).\3/.exec(x+'᛫'+(a=/^(((.*).*)\2+)\3\n/.exec(s)[1])+a))&&console.log(v[1].length,i))

Keluaran

1 1

Test Case 4

s="ababa\nbabab\nababb\nbabab"
s.split('\n').map((x,i)=>(v=/^(.*)(.)(.*)᛫.*\1(?!\2).\3/.exec(x+'᛫'+(a=/^(((.*).*)\2+)\3\n/.exec(s)[1])+a))&&console.log(v[1].length,i))

Keluaran

4 2

Test Case 5

s="xyxy\nyyxy"
s.split('\n').map((x,i)=>(v=/^(.*)(.)(.*)᛫.*\1(?!\2).\3/.exec(x+'᛫'+(a=/^(((.*).*)\2+)\3\n/.exec(s)[1])+a))&&console.log(v[1].length,i))

Keluaran

0 1

Test Case 6

s="ababaababa\nababaaaaba"
s.split('\n').map((x,i)=>(v=/^(.*)(.)(.*)᛫.*\1(?!\2).\3/.exec(x+'᛫'+(a=/^(((.*).*)\2+)\3\n/.exec(s)[1])+a))&&console.log(v[1].length,i))

Keluaran

6 1
MT0
sumber
Sayangnya, pendekatan ini gagal jika, misalnya s="xyxy\nyyxy",. Untuk baris kedua, match[4]akan menjadi yy; itu harus adil y.
Dennis
Bekerja kembali dan disingkat 14 karakter.
MT0
Sangat bagus! Saya telah mencoba regex kedua yang sama di beberapa titik, tetapi saya menambahkan pola minimal dua kali daripada yang maksimal (dan, dengan demikian, gagal sengsara). Satu masalah kecil: Regex pertama akan melaporkan ababpola ababaababa; Anda perlu menggunakan ^…$.
Dennis
/^…\n/bekerja atau/^…$/m
MT0
1
Mungkin tidak memerlukan yang utama ^(setidaknya tidak untuk 6 test case yang saya daftarkan - tetapi mungkin ada contoh yang berlawanan di mana ia melakukannya sehingga saya meninggalkannya).
MT0