Sửa lời nói lắp bắp của tôi


12

Nói lắp là một vấn đề mà nhiều người trong chúng ta có thể đã trải qua hoặc ít nhất là nhìn thấy nó. Mặc dù hầu hết các phần mềm nhận dạng giọng nói nổi tiếng đều có vấn đề nghiêm trọng với việc nói lắp, hãy tưởng tượng một phần mềm hiểu nói lắp, nhưng không thể sửa chúng và chỉ viết chúng như hiện tại.

Một ví dụ văn bản được viết bởi một phần mềm như vậy có thể như thế này: "xin hãy cẩn thận" . Trong ví dụ này "cẩn thận" là từ gốc và "ca ca" là những từ nói lắp.

Thử thách

Viết chương trình hoặc chức năng sửa các từ bị nói lắp bằng cách xóa chúng khỏi đầu vào trong khi vẫn giữ các từ gốc. Ví dụ: phiên bản cố định của "vui lòng cẩn thận" sẽ là "vui lòng cẩn thận" .

Đây là , câu trả lời ngắn nhất trong mọi ngôn ngữ đều thắng!

Những từ nói lắp là gì?

Nói lắp có nhiều biến thể khác nhau. Nhưng để đơn giản cho thử thách này, chúng tôi sẽ giới hạn nó theo các quy tắc sau:

  • Các từ nói lắp có thể là một phần chưa hoàn thành hoặc toàn bộ từ gốc. Bởi "phần chưa hoàn thành" Tôi có nghĩa là từ gốc phải bắt đầu chính xác với từ nói lắp. Ví dụ: "ope""open" cả hai có thể là một từ được nói lắp cho "open" , nhưng "pen" không thể là một từ vì "open" không bắt đầu bằng "pen" .
  • Các từ nói lắp phải chứa ít nhất một trong các nguyên âm "aeiou" . Ví dụ: "ngôi sao" có thể là một từ được nói lắp cho "bắt đầu" vì nó chứa "a" , nhưng "st" không thể là một từ bị nói lắp vì nó không chứa bất kỳ nguyên âm nào được đề cập.
  • Các từ nói lắp chỉ có thể xuất hiện trước từ gốc và phải được lặp lại ít nhất hai lần để hợp lệ (từ gốc không được tính trong các lần lặp lại). Ví dụ: "oo open" có các từ bị nói lắp nhưng "o open o" thì không, bởi vì "o" sau từ gốc không được tính và "o" trước khi từ gốc không được lặp lại ít nhất hai lần. "go go go go go go" có năm lần lặp lại các từ bị nói lắp trước từ gốc và có giá trị.
  • Một tập hợp các từ lặp đi lặp lại lặp đi lặp lại không thể chứa các dạng hỗn hợp và các từ phải giống hệt nhau. Ví dụ: "op o op open" không được tính là những từ nói lắp. Mặt khác, "o op op open" có những từ bị nói lắp bởi vì "o" đầu tiên được xem là một từ hoàn toàn khác ở đây và hai chữ "op" được tính là những từ được nói lắp là "mở" .
  • Trong trường hợp có nhiều tập hợp các từ lặp đi lặp lại hợp lệ ngay sau nhau, chỉ có từ gốc cuối cùng ở lại. Ví dụ, trong "ooo op op mở op" , các "oo o" một phần được xem như lời lắp bắp những người đầu tiên "op" , vì vậy họ cần được loại bỏ và sau đó "op op op" được xem như lời lắp bắp của "mở " Và chúng cũng nên được loại bỏ, vì vậy chỉ còn lại " mở " sau khi loại bỏ các từ nói lắp. Bạn có thể giả sử rằng nhiều bộ từ lặp đi lặp lại hợp lệ chỉ xảy ra từ trái sang phải, do đó, việc sửa "op op ooo open" sẽ dẫn đến "op op open" (aka

Đầu vào

  • Đầu vào là một chuỗi dòng duy nhất chỉ chứa các chữ cái tiếng Anh ASCII (az), chữ số (0-9) và ký tự khoảng trắng. Vỏ chữ không quan trọng và bạn có thể quyết định chấp nhận chữ thường hoặc chữ hoa hoặc cả hai, nhưng vỏ phải giữ nguyên và bạn không thể thay đổi nó ở đầu ra.
  • Bạn có thể sử dụng danh sách các chữ cái (như ["l","i","s","t"," ","o","f"," ","l","e","t","t","e","r","s"]) thay vì chuỗi, nhưng bạn không thể sử dụng danh sách các từ. Nếu ngôn ngữ của bạn có cấu trúc đầu vào khác, hãy sử dụng nó. Vấn đề là đầu vào không nên được phân tách bằng từ, vì vậy chi phí tách từ trong một số ngôn ngữ thực sự có thể kích hoạt các giải pháp sáng tạo khác.
  • Đầu vào có thể không chứa, một hoặc nhiều từ nói lắp trong đó.
  • Các từ và số được phân tách bằng một khoảng trắng và đầu vào sẽ không chứa khoảng trắng kép ngay cạnh nhau.

Đầu ra

  • Một chuỗi hoặc một danh sách các chữ cái hoặc cấu trúc phù hợp trong ngôn ngữ của bạn với tất cả các từ bị nói lắp được loại bỏ khỏi đầu vào.
  • Các từ đầu ra phải được phân tách bằng chính xác một khoảng trắng (giống như đầu vào).
  • Hàng đầu và dấu vết duy nhất hoặc dấu cách được cho phép.

Sơ hở tiêu chuẩn bị cấm.

Các trường hợp thử nghiệm

Không nói lắp:

"hello world" => "hello world"

Một ví dụ duy nhất của các từ nói lắp lặp đi lặp lại:

"ope ope ope ope open the window" => "open the window"

Nhiều trường hợp lặp lại các từ nói lắp:

"there is is is is something un un under the the the table" => "there is something under the table"

Không nói lắp, không lặp lại đủ:

"give me the the book" => "give me the the book"

Không nói lắp, không có bất kỳ nguyên âm nào được đề cập:

"h h help m m m me" => "h h help m m m me"

Những con số không nói lắp, chúng không có bất kỳ nguyên âm nào được đề cập:

"my nu nu number is 9 9 9 9876" => "my number is 9 9 9 9876"

Nhưng một từ có cả nguyên âm và số có thể có những từ nói lắp:

"my wi wi windows10 is slow" => "my windows10 is slow"

Các dạng khác nhau của các từ nói lắp trong cùng một nhóm lặp lại không được tính:

"this is an ant antarctica does not have" => "this is an ant antarctica does not have"

Đối với nhiều bộ từ liên tục được đặt ngay sau nhau, chỉ giữ lại từ gốc cuối cùng:

"what a be be be beauti beauti beautiful flower" => "what a beautiful flower"

Đây không phải là trường hợp nhiều tập hợp các từ lắp ghép liên tục ngay sau nhau:

"drink wat wat wa wa water" => "drink wat wat water"

Đầu vào trống:

"" => ""

Thêm trường hợp từ ý kiến:

"a ab abc" => "a ab abc"
"a ab ab abc" => "a abc"
"ab ab abc abcd" => "abc abcd"
"a a ab a able" => "ab a able"
"i have ave ave average" => "i have average"
"my wi wi windows 10 is cra cra crap" => "my windows 10 is crap"

Một danh sách dễ dàng để sao chép các trường hợp thử nghiệm ở trên:

"hello world",
"ope ope ope ope open the window",
"there is is is is something un un under the the the table",
"give me the the book",
"h h help m m m me",
"my nu nu number is 9 9 9 9876",
"my wi wi windows10 is slow",
"this is an ant antarctica does not have",
"what a be be be beauti beauti beautiful flower",
"drink wat wat wa wa water",
"",
"a ab abc",
"a ab ab abc",
"ab ab abc abcd",
"a a ab a able",
"i have ave ave average",
"my wi wi windows 10 is cra cra crap"

2
"drink wat wat wa wa water" => "drink wat wat water"Có vẻ như quy tắc nên áp dụng đệ quy để điều này trở thành "uống nước"
Jonah

2
@Jonah nếu bạn đọc mục cuối cùng bên dưới Từ bị nói lắp là gì? Tôi đã giải thích vấn đề này. "wat wat" không phải là những từ nói lắp cho "wa" và chúng tôi chỉ sửa một lần, vì vậy một khi chúng tôi nhận được "uống nước wat wat", chúng tôi không sửa lại để loại bỏ những từ bị nói lắp mới được hình thành. Nhưng trong trường hợp ngược lại như "wa wa wat wat water" sẽ là "water" bởi vì "wa wa" là những từ được nói lắp cho "wat" và "wat wat" đầu tiên cũng là những từ được nói lắp của "nước".
Đêm2

Ok đủ công bằng, tôi chỉ nói rằng sẽ có ý nghĩa để tiếp tục sửa chữa cho đến khi bạn không thể nữa, nhưng tôi có thể thấy đối số để tập trung vào một lần lặp duy nhất là tốt.
Giô-na

Câu trả lời:


6

C (gcc), 183 180 178 byte

f(s,t,u,T,e,r)char*s,*t,*u,*r;{for(;s=index(u=s,32);T>1&strpbrk(u,"aeiou")-1<s&&memmove(s=u,t-e,r-t-~e))for(e=++s-u,r=u+strlen(t=u),T=0;(t+=e)<r&!memcmp(u,t,e-1)&t[-1]==32;++T);}

Hãy thử trực tuyến!

Chà, C chắc chắn không thể cạnh tranh với sự ngắn gọn của regex ...

Điều này đặc biệt khó đọc vì cuối cùng tôi đã thu gọn toàn bộ chức năng thành một cặp forvòng lặp lồng nhau (không có cơ thể!). Điều đó làm cho thứ tự đánh giá trở nên mạnh mẽ - mã gần đầu thực sự thực thi cuối cùng.

Thủ thuật yêu thích của tôi ở đây là strpbrk(u,"aeiou")-1<s. Điều này được sử dụng để kiểm tra xem từ lặp đi lặp lại có chứa nguyên âm hay không. uchỉ vào điểm bắt đầu của từ lặp lại và schỉ đến sự lặp lại thứ hai của từ đó; ví dụ:

"my nu nu number is 9 9 9 9876"
    ^  ^
    u  s

strpbrksau đó tìm thấy ký tự đầu tiên "aeiou"xuất hiện sau đó u. (Trong trường hợp này, đó là 'u'ngay sau đó.) Sau đó, chúng tôi có thể kiểm tra xem điều này có trước sđể xác minh từ có chứa nguyên âm hay không. Nhưng có một vấn đề nhỏ - strpbrktrả về NULL(tức là 0) nếu không có nguyên âm trong toàn bộ chuỗi. Để khắc phục điều này, tôi chỉ cần trừ đi 1, biến 0thành 0xffffffffffffffff(trên máy của tôi) do tràn. Là giá trị tối đa của một con trỏ, điều này được quyết định lớn hơn s, khiến cho việc kiểm tra thất bại.

Đây là một phiên bản cũ hơn một chút (trước khi chuyển đổi làm rối loạn dòng điều khiển) với các bình luận:

f(s,t,u,c,n,e)char*s,*t,*u,*e;{
    // set s to the position of the *next* check; u is the old position
    for(;s=index(u=s,32);) {
        // count the length of this word (incl. space); also fix s
        n=++s-u;
        // find the end of the string; assign temp pointer to start
        e=u+strlen(t=u);
        // count repetitions of the word
        for(c=0;                // number of repetitions
            (t+=n)              // advance temp pointer by length of word
            <e&&                // check that we haven't hit the end...
            !strncmp(u,t,n-1)&& // ...and the word matches...
            t[-1]==32;          // ...and the previous character was space
            ++c);               // if so, increment count
        // decide whether to remove stuttering
        c>1&&                    // count must be at least 2
        strpbrk(u,"aeiou")-1<s&& // word must contain a vowel
        // if so, move everything after the last occurrence back to the
        // beginning, and also reset s to u to start scanning from here again
        memmove(s=u,t-n,e-t+n+1);
    }
}

Cảm ơn @ user1485369 cho 3 byte và @ceilingcat cho 2 byte.


-3 byte bằng cách thay thế T>1&&strpbrkbằng T>1&strpbrk, r&&!strncmpvới r&!strncmp&&t[-1]bằng &t[-1].
girobuz


@ceilingcat Liên kết của bạn không thành công trong một số trường hợp thử nghiệm, nhưng 2 trong số 3 tối ưu hóa đó hoạt động; cảm ơn!
Doorknob

Đề xuất bcmp()thay vìmemcmp()
trần mèo

4

Perl 5 (-p), 34 byte

Dựa trên câu trả lời đã bị xóa của Arnauld.

s/(\b(\w*[aeiou]\w*) )\1+(?=\2)//g

Hãy thử trực tuyến!


Điều này tạo ra "zab" cho "za a ab". Tôi không nghĩ rằng nên có một sự nói lắp được phát hiện trong đầu vào đó.
đệ quy

@recursive cảm ơn, đã sửa.
Grimmy

2
Tôi đã xem xét các trường hợp thử nghiệm và nghĩ ra một regex, chỉ để thấy nó đã ở đây. Đương nhiên, điều này có nghĩa là cổng Retina tầm thường là 30 byte.
Neil

3

05AB1E , 30 29 28 byte

-1 byte nhờ Kevin Cruijssen

#Rγε®y¬©žMÃĀiнkĀDygαΘ+∍]R˜ðý

Hãy thử trực tuyến!

05AB1E, không có regexes, chắc chắn không giống như công cụ tốt nhất cho nhiệm vụ này. Tuy nhiên, bằng cách nào đó nó chỉ xoay sở được với Retina.

#                     # split on spaces
 R                    # reverse the list of words
  γ                   # group consecutive identical words together

ε                   ] # for each group of words y:
 ®                    #  push the previous word on the stack (initially -1)
  y                   #  push another copy of y
   ¬                  #  push the first element without popping
    ©                 #  save the current word for the next loop
     žM               #  built-in constant aeiou
       ÃĀi          ] #  if the length of the intersection is non-zero:
           н          #   take the first element of y
            kĀ        #   0 if the previous word starts with this word, 1 otherwise
              D       #   duplicate
               yg     #   length of y (the number of consecutive identical words)
                 α    #   subtract the result of the startsWith check
                  Θ   #   05AB1E truthify (1 -> 1, anything else -> 0)
                   +  #   add the result of the startsWith check
                    ∍ #   set the length of y to that value
                      #  otherwise leave y unchanged

˜                     # flatten the modified list of groups of words
 R                    # reverse the list of words
  ðý                  # join with spaces

1
Bạn có thể loại bỏ gtrước Ā. Sự thật theo kiểu Python sẽ dẫn đến 0các chuỗi rỗng và 1cho các chuỗi không trống.
Kevin Cruijssen

@KevinCruijssen tìm thấy tốt đẹp!
Grimmy


1

Stax , 26 byte

å╬↓<▀.₧▀"╦n▐∞↨vß%ù:Qa3@=↔_

Chạy và gỡ lỗi nó

Cổng trực tiếp từ câu trả lời perl của @ Grimy. Stax có thể thu nhỏ mô hình regex theo nghĩa đen và nó có hằng số nguyên âm có khả năng thu nhỏ [aeiou].


1

Sạch , 184 byte

import StdEnv,Data.List,Text
$s=join[' '](f(group(split[' ']s)))
f[[a]:t]=[a:f t]
f[h=:[a:_]:t=:[[b:_]:_]]|intersect['aeiou']a==[]=h++f t|isPrefixOf a b=f t=if(h>[a,a])[a]h++f t
f[]=[]

Hãy thử trực tuyến!

Các định nghĩa $ :: [Char] -> [Char], phân tách chuỗi đầu vào trên các khoảng trắng và các nhóm phần tử giống hệt nhau sau đó được người trợ giúp thu gọn lại f :: [[[Char]]] -> [[Char]], tham gia trước khi quay lại.

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.