问题描述
在广告平台中,为了给广告主一定的自由性和效率,允许广告主在创造标题的时候以通配符的方式进行创意提交。线上服务的时候,会根据用户的搜索词触发的 bidword 对创意中的通配符(通配符是用成对 {} 括起来的字符串,可以包含 0 个或者多个字符)进行替换,用来提升广告投放体验。例如:“{末日血战} 上线送 SSR 英雄,三天集齐无敌阵容!”,会被替换成“帝国时代游戏下载上线送 SSR 英雄,三天集齐无敌阵容!”。给定一个含有通配符的创意和n个标题,判断这句标题是否从该创意替换生成的。
简单来说,我们有一个广告创意模板,这个模板可以包含通配符部分 {},代表可以匹配任意内容。我们的任务是检查给定的一系列标题是否符合这个模板。
解决方案
-
理解模板的意义:
- 模板中的固定部分表示精确文本匹配。
{}表示可以被任意字符替换的部分。
-
将模板转换为正则表达式:
- 为了匹配任意长度的任何字符,
{}通配符需要被正则表达式中的.*替换,因为.*在正则表达式中表示“匹配任何字符任意次”。
- 为了匹配任意长度的任何字符,
-
转义模板中的特殊字符:
- 正则表达式中一些字符(如
.、*、+、?、(、)、[、]、{、}、^、$等)有特殊含义。
- 正则表达式中一些字符(如
-
构建正则表达式:
- 使用
re.sub(r'{[^{}] *}', '.* ', template)转义模板中的文字部分。
- 使用
-
编译正则表达式:
- 使用正则表达式库(如 Python 的
re库)编译正则表达式,提高匹配效率。
- 使用正则表达式库(如 Python 的
-
匹配标题:
- 对每个标题应用编译好的正则表达式。
- 使用正则表达式的匹配功能,检查标题是否符合模板。
-
记录匹配结果:
- 将符合条件的标题保存到一个列表中,方便后续使用或分析。
代码
根据以上思路,可以使用下面的 Python 代码来实现:
import re
def solution(n, template, titles):
# 将模板中的通配符替换为正则中的.*
regex_pattern = re.sub(r'\{[^{}]*\}', '.*', template)
# 遍历所有标题,检查是否匹配生成的正则表达式
results = []
for title in titles:
if re.fullmatch(regex_pattern, title):
results.append("True")
else:
results.append("False")
return ','.join(results)
# 测试样例
n1, template1, titles1 = 4, "ad{xyz}cdc{y}f{x}e", ["adcdcefdfeffe", "adcdcefdfeff", "dcdcefdfeffe", "adcdcfe"]
n2, template2, titles2 = 3, "a{bdc}efg", ["abcdefg", "abefg", "efg"]
n3, template3, titles3 = 5, "{abc}xyz{def}", ["xyzdef", "abcdef", "abxyzdef", "xyz", "abxyz"]
# 输出结果
print(solution(n1, template1, titles1)) # 输出: "True,False,False,True"
print(solution(n2, template2, titles2)) # 输出: "True,True,False"
print(solution(n3, template3, titles3)) # 输出: "True,False,True,True,True"
正则表达式的组成部分
-
\{和\}:{和}是正则表达式中的元字符,需要通过外加反斜杠进行转义。在这里,\{表示匹配一个 '{' 字符,\}表示匹配一个 '}' 字符。
-
[^{}]*:[^...]是字符集否定符号,表示匹配任意不在方括号内的字符。[^{}]*表示匹配零个或多个不包含{或}的字符。
正则流程解释
re.sub(r'\{[^{}]*\}', '.*', template):r'\{[^{}]*\}'这个正则表达式整体匹配{和}之间的任意字符。re.sub函数会将匹配到的部分用.*替换。.*是正则表达式中的通配符,表示匹配任意字符(除了换行符)零次或多次。
假设 template 为 "ad{xyz}cdc{y}f{x}e",那么:
\{[^{}]*\}用{xyz}匹配成功,替换为.*,ad.*cdc{y}f{x}e中再次匹配{y}和{x}为.*,
最终得到的正则表达式为 ad.*cdc.*f.*e。生成的 regex_pattern = "ad.*cdc.*f.*e" 正则表达式,用于匹配目标字符串是否符合广告创意的规则。这段代码就可以将输入的包含通配符的模板转换为正则表达式格式,以便后续进行匹配判断。
当然,下面是一些常见的正则表达式示例及其说明:
常用的正则表达式
- 匹配字符 "abc"
匹配包含字符串 "abc" 的文本。abc
字符集
-
匹配元音字母
[aeiou]匹配任一元音字母 (a, e, i, o, u)。
-
匹配除元音字母之外的字母
[^aeiou]匹配除 (a, e, i, o, u) 以外的任意字符。
字符类
- 匹配任何字母和数字
匹配小写字母、大写字母或数字。[a-zA-Z0-9]
预定义字符类
-
匹配任何数字
\d等价于
[0-9]。 -
匹配任何非数字
\D等价于
[^0-9]。 -
匹配空白字符
\s匹配空格、制表符、换页符等。
-
匹配非空白字符
\S匹配任何非空白字符。
边界匹配
-
匹配行首
^abc匹配以 "abc" 开头的字符串。
-
匹配行尾
abc$匹配以 "abc" 结尾的字符串。
重复匹配
-
匹配零次或多次
ab*c匹配零个或多个 b 后跟一个 c,比如 "ac", "abc", "abbc" 等。
-
匹配一次或多次
ab+c匹配一个或多个 b 后跟一个 c,比如 "abc", "abbc" 等。
-
匹配零次或一次
ab?c匹配零个或一个 b 后跟一个 c,比如 "ac" 或 "abc"。
-
匹配指定次数
ab{2}c匹配恰好两个 b 后跟一个 c,比如 "abbc"。
捕获组
-
捕获组
(abc)匹配并捕获 "abc"。
-
使用组进行匹配和捕获
(ab)c\1匹配 "abcab"。
\1引用第一个捕获组的内容。
选择性匹配
- 或操作符
匹配 "abc" 或 "def"。abc|def
贪婪与懒惰匹配
-
贪婪匹配
a.*b匹配从第一个 a 到最后一个 b 之间的所有字符。
-
懒惰匹配
a.*?b匹配从第一个 a 到第一个 b 之间的最少字符。
实际示例
-
邮箱验证
^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$匹配有效的电子邮箱地址。
-
电话号码验证(中国大陆)
^1[3456789]\d{9}$匹配有效的中国大陆手机号码,手机号码以 1 开头,第二位数是 3-9,后面跟着9位数字。
刷题感悟
-
正则表达式的强大性和复杂性:
- 理解与应用:正则表达式是一种强大的工具来处理字符串匹配和解析任务。我了解到如何将固定格式的模板转换为能够匹配多种格式的通用正则表达式。
- 灵活性:通过使用通配符和锚点符号,正则表达式可以灵活地处理文字的开头、中间和结尾匹配。
-
逐步解决复杂问题:
- 分解问题:将复杂问题分解为更小的步骤,例如首先处理固定文本部分,再处理可变通配符部分,并逐步构建解决方案。
- 调试和验证:通过调试和测试,逐步验证每个步骤的正确性,是确保最终解决方案有效的重要手段。
-
使用Python正则库的技巧:
re.sub():学会使用re.sub()来替换需要处理的字符串。re.fullmatch():区别fullmatch和其他匹配函数,确保整个字符串完全匹配是解决问题的关键。
-
处理边界情况:
- 理解如何处理不同输入条件下的极端或边界情况,是确保解决方案健壮性的重要因素。这包括处理输入中的空字符、过多或过少的匹配字符等。