修正口吃的话


12

口吃是我们许多人可能已经经历或至少看到的问题。尽管大多数著名的语音识别软件都存在口吃的严重问题,但让我们想象一下一种软件,该软件可以理解口吃但无法修复口吃,只能照原样编写。

用这样的软件编写的示例文字可能是这样的:“请小心”。在此示例中,“小心”是原始单词,“ ca ca”是断断续续的单词。

挑战

编写一个程序或函数,通过保留输入中的原始单词,通过从输入中删除它们来修正出现的单词。例如,“请小心”的固定版本将是“请小心”

这是,每种语言的最短答案胜出!

什么是口吃的单词?

口吃有许多不同的变化。但是为了简化此挑战,我们将其限制为以下规则:

  • 口吃的单词可能是原始单词的未完成部分或全部。所谓“未完成的部分”,是指原始单词应完全以口吃单词开头。例如“OPE”“开放”既可以是一个结巴字的“开放”,但“笔”不能因为一个“开放”不是以“笔”
  • 口吃的单词必须至少包含一个“ aeiou”元音。例如,“ star”可以是“ start”的口吃词,因为它包含“ a”,但是“ st”不能是口吃的词,因为它不包含任何上述元音。
  • 口吃的单词只能出现在原始单词之前,应重复至少两次才能有效(原始单词不计入重复中)。例如,“ oo open”具有口吃的单词,但没有“ o open o”,因为原始单词之后的“ o”不计算在内,原始单词之前的“ o”没有重复至少两次。“ go go go go go go go”在原始单词之前有五个重复的口吃单词,并且有效。
  • 一组重复的口吃单词不能包含混合形式,并且单词应该彼此完全相同。例如,“ op o op open”不算作口吃的单词。另一方面,“ o op op open”具有口吃单词,因为在这里第一个“ o”被视为一个完全不同的单词,而两个“ op”被视为“ open”的口吃单词。
  • 如果有多个有效的重复设置的紧接单词,则仅保留最后一个原始单词。例如,在“ ooo op op op open”中“ oo o”部分被视为第一个“ op”的口吃单词,因此应将其删除,然后将“ op op op”视为“ open”的口吃单词。,也应将其删除,因此在删除口吃的单词后仅保留“ open”字样。您可以假设多个有效的重复口吃单词集仅从左到右发生,因此修复“ op op ooo open”将导致“ op op open”(aka

输入值

  • 输入是仅包含ASCII英文字母(az),数字(0-9)和空格字符的单行字符串。字母大小写并不重要,您可以决定接受小写​​或大写字母或两者都接受,但是大写字母应保持不变,并且不能在输出中进行更改。
  • 您可以使用字母列表(例如["l","i","s","t"," ","o","f"," ","l","e","t","t","e","r","s"])代替字符串,但是不能使用单词列表。如果您的语言具有不同的输入结构,请使用它。关键是输入不应该用单词分隔,因此以某些语言分隔单词的成本实际上可能会触发其他创造性的解决方案。
  • 输入中可能不包含任何一个或多个口吃的单词。
  • 单词和/或数字用单个空格分隔,输入将不包含彼此相邻的双精度空格。

输出量

  • 字符串或字母列表或您语言中的适当结构,其中所有卡住的单词已从输入中删除。
  • 输出字应精确隔开一个空格(与输入相同)。
  • 允许使用单个前导和尾随的换行符或空格。

禁止出现标准漏洞

测试用例

没有口吃的话:

"hello world" => "hello world"

重复口吃单词的一个实例:

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

重复口吃单词的多个实例:

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

没有口吃的话,没有足够的重复:

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

没有口吃的单词,没有任何上述元音:

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

数字不是口吃的单词,它们没有任何上述元音:

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

但是一个带有元音和数字的单词可能会结结巴巴的单词:

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

同一重复组中不同形式的口吃单词不计算在内:

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

对于紧接在一起的多个连续的口吃单词,仅保留最后一个原始单词:

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

这不是紧接在后的多个连续的套结单词的情况:

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

空输入:

"" => ""

来自评论的更多案例:

"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"

易于复制的上述测试用例列表:

"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"似乎确实应该以递归方式应用该规则,以使其成为“饮用水”
约拿(Jonah)

2
@Jonah如果您读了什么是口吃单词下的最后一项我已经解释了这个问题。“ wat wat”不是“ wa”的口吃单词,我们只修复了一次,因此一旦获得“ drink wat wat water”,我们就不再修复以去除新形成的口吃单词。但是在相反的情况下,例如“ wa wa wat wat water”,结果将是“ water”,因为“ wa wa”是第一个“ wat”的口吃单词,而“ wat wat”也是“ water”的口吃单词。
2点

好的,很公平,我只是说保持固定直到您不再需要它都是有意义的,但是我也看到了关注单个迭代的观点。
乔纳

Answers:


6

C(GCC),183个 180 178字节

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

在线尝试!

好吧,C当然不能与正则表达式的简洁竞争。

这一点特别难读,因为我最终将整个功能折叠成一对嵌套的for循环(没有主体!)。这使得评估顺序全都变了-开头附近的代码实际上最后执行了。

我最喜欢的把戏是strpbrk(u,"aeiou")-1<s。这用于检查重复的单词是否包含元音。u指向重复单词的开头,并s指向单词的第二次重复;例如:

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

strpbrk然后找到"aeiou"出现在之后的第一个字符u。(在这种情况下,它'u'紧接在后。)然后我们可以检查它s是否在此之前,以验证单词中是否包含元音。但是有一个小问题- 如果整个字符串中没有元音,则strpbrk返回NULL(即0)。为了解决这个问题,我简单地减1,它变成00xffffffffffffffff(我的机器上),由于溢出。作为指针的最大值,它肯定大于s,从而导致检查失败。

这是一个稍旧的版本(在使控制流变得混乱的转换之前),并带有注释:

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

感谢@ user1475369提供了3个字节,而@ceilingcat提供了2个字节。


-3通过替换字节T>1&&strpbrkT>1&strpbrkr&&!strncmpr&!strncmp,和&&t[-1]&t[-1]
girobuz


@ceilingcat您的链接在某些测试用例中失败,但是这3个优化中的2个有效;谢谢!
门把手

建议bcmp()而不是memcmp()
ceilingcat '19

4

Perl 5(-p),34个字节

基于Arnauld删除的答案。

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

在线尝试!


这将为“ za a ab”产生“ zab”。我认为在该输入中不应检测到断断续续。
递归

@recursive谢谢,已修复。
Grimmy19年

2
我看了看测试用例并设计了一个正则表达式,才发现它已经在这里。自然,这意味着琐碎的Retina端口为30个字节。
尼尔

3

05AB1E30 29 28字节

-1字节感谢Kevin Cruijssen

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

在线尝试!

没有正则表达式的05AB1E绝对看起来不是完成此任务的最佳工具。尽管如此,它还是设法勉强击败了视网膜。

#                     # 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
您可以删除g之前的Ā。Python样式的trueify将已经导致0空字符串和1非空字符串。
凯文·克鲁伊森

@KevinCruijssen很高兴找到!
Grimmy19年

1

Perl 6,45个字节

{S:g/<|w>(\S*<[aeiou]>\S*)\s$0+%%\s{}<?$0>//}

在线尝试!

一个简单的正则表达式答案,用空字符串替换口吃的所有匹配项。


1

Stax,26 个字节

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

运行并调试

来自@Grimy的perl答案的直接端口。Stax能够缩小regex模式文字,并且它具有能够缩小的元音常量[aeiou]


1

干净,184字节

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[]=[]

在线尝试!

Defines $ :: [Char] -> [Char],它在空格上分割输入字符串,并将相同的元素分组,然后由助手折叠起来f :: [[[Char]]] -> [[Char]],并在返回之前合并。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.