正则之反向引用
前言
某日在逛so时,发现侧边栏的Hot Network Questions里有一例codegolf的问题Does it repeat?。
挑战
好奇之下点入观看,该题主的挑战如下:
当一条字符串中含有2(组/个)连续的字符时,该字符串可以称之为“连续的字符串”。
例:2034384538452可以成为“连续的字符串”,因为其含有2次连续的3845。
请找出能够判断“连续的字符串”的方法,输入可以用字符串或数组,输入不能为空,并且字符串长度必须大于等于1。
题主使用1与0来区分true和false,挑战者也可以使用其他不同的值来区分true/false。
123456 abcab -> 0bdefdefg -> 1Hello, World! -> 1pp.pp/pp -> 1q -> 021020121012021020120210121020121012021012102012021020121012021020120210121020120210201210120210121020121012021020120210121020121012021012102012021020121012021012102012101202102012021012102012021020121012021020120210121020121012021012102012021020121012021020120210121020120210201210120210121020121012021020120210121020120210201210120210201202101210201210120210121020120210201210120210121020121012021020120210121020121012021012102012021020121012021020120210121020120210201210120210121020121012021020120 -> 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进行快速尝试
匹配出3连的连续字符串
匹配出html标签内的文字
总结
反向匹配在业务代码中能用的地方不是很多,假如是纯粹去掉html标签的话,php有strip_tags
,python也有对应的stripogram
包,简略用法看这里。
多学一点,在leetcode
或codewars
这类的刷题网站里以最少的代码量达到要求也能有成就感。
在这里挂个我的codewars肩章,欢迎一起来玩儿。