前言
某日在逛stackoverflow時,發現側邊欄的Hot Network Questions里有一例codegolf的問題Does it repeat?。
挑戰
好奇之下點入觀看,該題主的挑戰如下:
當一條字符串中含有2(組/個)連續的字符時,該字符串可以稱之為“連續的字符串”。
例:2034384538452可以成為“連續的字符串”,因為其含有2次連續的3845。
請找出能夠判斷“連續的字符串”的方法,輸入可以用字符串或數組,輸入不能為空,並且字符串長度必須大於等於1。
題主使用1與0來區分true和false,挑戰者也可以使用其他不同的值來區分true/false。abcab -> 0 bdefdefg -> 1 Hello, World! -> 1 pp.pp/pp -> 1 q -> 0 21020121012021020120210121020121012021012102012021020121012021020120210121020120210201210120210121020121012021020120210121020121012021012102012021020121012021012102012101202102012021012102012021020121012021020120210121020121012021012102012021020121012021020120210121020120210201210120210121020121012021020120210121020120210201210120210201202101210201210120210121020120210201210120210121020121012021020120210121020121012021012102012021020121012021020120210121020120210201210120210121020121012021020120 -> 0
解法
所有答者中,大部分常用編程語言的答者都使用正則進行判斷,例如:
PHP的解法
<?=preg_match('#(.+)\1#',$argn);
import re re.compile(r'(.+)\1').search
s=>/(.+)\1/.test(s)
a->a.matches(".*(.+)\\1.*")
雖然也有使用循環對比來解答的,但這不是我的關注點。
所有正則的解法中,歸根結底就是6個字符(.+)\1.+和()的意思好理解,畢竟是常用的。\1雖然能猜到用途,但是無論如何想不起來是干啥的,進入查詢模式。
反向引用(backreference)
根據揭開正則表達式的神秘面紗一文,發現原來正則除了貪婪和非貪婪外還有名為“反向引用”的高級規則。
表達式在匹配時,表達式引擎會將小括號 “( )” 包含的表達式所匹配到的字符串記錄下來。在獲取匹配結果的時候,小括號包含的表達式所匹配到的字符串可以單獨獲取。
“\1” 引用第1對括號內匹配到的字符串,”\2” 引用第2對括號內匹配到的字符串……以此類推。
如果一對括號內包含另一對括號,則外層的括號先排序號。換句話說,哪一對的左括號 “(“ 在前,那這一對為先。
在正則(.+)\1中,\1等於(.+)中匹配到的值,也就是連續2次相同的值。
嘗試
使用Python進行快速嘗試
def fa(regex,subject): return re.findall(regex,subject);
匹配出3連的連續字符串
fa(r'(.+)\1\1','d123123123e'); #['123'] fa(r'(.+)\1\1','d112233e'); #[] #或 fa(r'((.+))\1\2','d123123123e'); #['123']
匹配出html標簽內的文字
fa(r'.*<(.+).*?>(.*)<\/\1>.*','asdwas<td>ssd</td>sdasdd'); #[('td', 'ssd')] fa(r'.*<(.+).*?(?:href="(.*?)".*?)?>(.*)<\/\1>.*','asdwas<a href="/a/b">find more</a>sda'); #[('a', '/a/b','find more')] fa(r'.*<(.+).*?(?:href="(.*?)".*?)?>(.*)<\/\1>.*','asdwas<a title="c" href="/a/b" class="on">find more</a>sda'); #[('a', '/a/b', 'find more')]
總結
反向匹配在業務代碼中能用的地方不是很多,假如是純粹去掉html標簽的話,php有strip_tags,python也有對應的stripogram包,簡略用法看這里。
多學一點,在leetcode或codewars這類的刷題網站里以最少的代碼量達到要求也能有成就感。
在這里掛個我的codewars肩章,歡迎一起來玩兒。
Reference
Does it repeat? - CodeGolf
揭開正則表達式的神秘面紗 - regexlab
備注:本文發布於2017-06-14,我github page的原文地址。
