题目
在广告平台中,为了给广告主一定的自由性和效率,允许广告主在创建标题时使用通配符。通配符用成对的 {} 括起来,可以包含0个或多个字符。线上服务时,会根据用户的搜索词触发的 bidword 对创意中的通配符进行替换,用来提升广告投放体验。例如,创意 "末日血战} 上线送 SSR 英雄,三天集齐无敌阵容!" 可以被替换成 "帝国时代游戏下载上线送 SSR 英雄,三天集齐无敌阵容!"。
题目描述
给定一个含有通配符的创意和 n 个标题,判断这些标题是否是从该创意替换生成的。通配符 {} 可以匹配任意长度的字符串,包括空字符串。
解题思路
-
提取模板中的固定部分和通配符部分:
- 遍历模板字符串,提取固定部分和通配符部分。
- 固定部分是指不在
{}中的部分,通配符部分是指在{}中的部分。
-
匹配标题:
- 使用递归函数
match来匹配标题。 - 对于固定部分,检查标题是否以该固定部分开头。
- 对于通配符部分,尝试匹配任意长度的字符串,递归检查剩余部分。
- 使用递归函数
-
处理异常情况:
- 确保模板中没有嵌套的通配符。
- 确保模板中的通配符成对出现。
代码详解
提取模板中的固定部分和通配符部分
def is_generated_from_template(template, title):
# 提取模板中的固定部分和通配符部分
parts = []
buffer = []
in_wildcard = False
for char in template:
if char == '{':
if in_wildcard:
raise ValueError("Nested wildcards are not allowed.")
if buffer:
parts.append(('fixed', ''.join(buffer)))
buffer = []
in_wildcard = True
elif char == '}':
if not in_wildcard:
raise ValueError("Unmatched closing wildcard bracket.")
if buffer:
parts.append(('wildcard', ''.join(buffer)))
buffer = []
in_wildcard = False
else:
buffer.append(char)
if buffer:
parts.append(('fixed', ''.join(buffer)))
if in_wildcard:
raise ValueError("Unmatched opening wildcard bracket.")
-
初始化:
parts列表用于存储模板的各个部分。buffer用于缓存当前字符。in_wildcard标记当前是否在通配符中。
-
遍历模板字符串:
- 如果遇到
{,检查是否已经在通配符中,如果是则抛出异常。否则,将buffer中的固定部分添加到parts中,并清空buffer,设置in_wildcard为True。 - 如果遇到
},检查是否在通配符中,如果不是则抛出异常。否则,将buffer中的通配符部分添加到parts中,并清空buffer,设置in_wildcard为False。 - 如果遇到其他字符,将其添加到
buffer中。
- 如果遇到
-
处理剩余字符:
- 最后检查
buffer是否有剩余字符,如果有则将其作为固定部分添加到parts中。 - 检查
in_wildcard是否为True,如果是则抛出异常。
- 最后检查
匹配标题
# 匹配标题
def match(parts, title):
i = 0
for part_type, part in parts:
if part_type == 'fixed':
if title.startswith(part, i):
i += len(part)
else:
return False
elif part_type == 'wildcard':
# 通配符可以匹配任意长度的字符串
j = i
while j <= len(title):
if match(parts[1:], title[j:]):
return True
j += 1
return False
return i == len(title)
-
初始化索引:
i初始化为0,表示当前匹配的位置。
-
遍历
parts列表:- 如果当前部分是固定部分,检查
title是否以该固定部分开头,如果是则移动索引i,否则返回False。 - 如果当前部分是通配符部分,尝试匹配任意长度的字符串,递归检查剩余部分。如果匹配成功则返回
True,否则继续尝试。
- 如果当前部分是固定部分,检查
-
检查匹配结果:
- 最后检查索引
i是否等于title的长度,如果是则返回True,否则返回False。
- 最后检查索引
主函数
def solution(n, template, titles):
results = []
for title in titles:
results.append(is_generated_from_template(template, title))
return ",".join("True" if result else "False" for result in results)
-
初始化结果列表:
results列表用于存储每个标题的匹配结果。
-
遍历标题:
- 对每个标题调用
is_generated_from_template函数,将结果添加到results列表中。
- 对每个标题调用
-
返回结果:
- 将
results列表中的布尔值转换为字符串,并用逗号连接成一个字符串返回。
- 将
测试用例解析
测试用例1
输入:template = "ad{xyz}cdc{y}f{x}e", titles = ["adcdcefdfeffe", "adcdcefdfeff", "dcdcefdfeffe", "adcdcfe"]
-
提取模板部分:
parts = [('fixed', 'ad'), ('wildcard', 'xyz'), ('fixed', 'cdc'), ('wildcard', 'y'), ('fixed', 'f'), ('wildcard', 'x'), ('fixed', 'e')]
-
匹配标题:
"adcdcefdfeffe":匹配成功。"adcdcefdfeff":匹配失败。"dcdcefdfeffe":匹配失败。"adcdcfe":匹配成功。
-
结果:
"True,False,False,True"
测试用例2
输入:template = "{xxx}h{cQ}N{vF}u{XTA}S{NTA}MLCq{yyy}", titles = ["CLSomGhcQNvFuzENTAMLCqxBdj", "CLSomNvFuXTASzENTAMLCqxBdj", "CLSomFuXTASzExBdj", "CLSoQNvFuMLCqxBdj", "SovFuXTASzENTAMLCq", "mGhcQNvFuXTASzENTAMLCqx"]
-
提取模板部分:
parts = [('wildcard', 'xxx'), ('fixed', 'h'), ('wildcard', 'cQ'), ('fixed', 'N'), ('wildcard', 'vF'), ('fixed', 'u'), ('wildcard', 'XTA'), ('fixed', 'S'), ('wildcard', 'NTA'), ('fixed', 'MLCq'), ('wildcard', 'yyy')]
-
匹配标题:
"CLSomGhcQNvFuzENTAMLCqxBdj":匹配失败。"CLSomNvFuXTASzENTAMLCqxBdj":匹配失败。"CLSomFuXTASzExBdj":匹配失败。"CLSoQNvFuMLCqxBdj":匹配失败。"SovFuXTASzENTAMLCq":匹配失败。"mGhcQNvFuXTASzENTAMLCqx":匹配成功。
-
结果:
"False,False,False,False,False,True"
测试用例3
输入:template = "a{bdc}efg", titles = ["abcdefg", "abefg", "efg"]
-
提取模板部分:
parts = [('fixed', 'a'), ('wildcard', 'bdc'), ('fixed', 'efg')]
-
匹配标题:
"abcdefg":匹配成功。"abefg":匹配成功。"efg":匹配失败。
-
结果:
"True,True,False"