25 创意标题匹配
在广告平台中,为了给广告主一定的自由性和效率,允许广告主在创造标题的时候以通配符的方式进行创意提交。线上服务的时候,会根据用户的搜索词触发的 bidword 对创意中的通配符(通配符是用成对 {} 括起来的字符串,可以包含 0 个或者多个字符)进行替换 ,用来提升广告投放体验。例如:“{末日血战} 上线送 SSR 英雄,三天集齐无敌阵容!”,会被替换成“帝国时代游戏下载上线送 SSR 英雄,三天集齐无敌阵容!”。给定一个含有通配符的创意和一句标题,判断这句标题是否从该创意替换生成的。
输入描述
输入包含以下部分:
- 第一行:一个整数
N,代表有N个标题。 - 第二行:一个字符串,代表含有通配符的创意,创意中包含大小写英文字母和成对的花括号
{}。 - 接下来的
N行:每行一个字符串,代表一个标题,每个标题只包含大小写英文字母。
限制条件:
1 <= N <= 10- 每个标题和创意的长度均小于等于
100 - 通配符
{}内的字符串可以为空或包含多个字符
输出描述
输出 N 行,每行输出 "True" 或 "False",判断对应的标题是否可以通过替换创意中的通配符生成。
示例
示例 1
输入
4
ad{xyz}cdc{y}f{x}e
adcdcefdfeffe
adcdcefdfeff
dcdcefdfeffe
adcdcfe
输出
True
False
False
True
说明
adcdcefdfeffe可以看作ad{}cdc{efd}f{eff}e或者ad{}cdc{efdfe}f{f}e,符合创意ad{xyz}cdc{y}f{x}e。adcdcefdfeff和dcdcefdfeffe不符合创意的替换规则。adcdcfe可以看作ad{}cdc{}f{}e,符合创意。
解题思路
本问题需要判断给定的标题是否可以通过替换创意中的通配符 {} 生成。每个通配符可以替换为任意长度(包括空字符串)的任意字符序列。为此,可以将创意字符串分解为固定部分和通配符部分,然后逐一匹配标题。
1. 解析创意字符串
首先,将创意字符串分解为固定部分和通配符部分。通配符用 {} 括起来,固定部分为字母序列。例如,创意 ad{xyz}cdc{y}f{x}e 可以分解为:
- 固定部分:
"ad","cdc","f","e" - 通配符部分:
"{xyz}","{y}","{x}"
2. 匹配逻辑
对于每个标题,按照解析后的顺序与创意进行匹配:
- 固定部分匹配:标题中对应位置必须精确匹配固定部分。
- 通配符部分匹配:通配符可以匹配标题中的任意长度字符串(包括空字符串)。
匹配过程可以采用递归回溯的方法,尝试所有可能的通配符替换方式,判断是否存在一种替换方式使得整个标题符合创意模板。
3. 递归匹配函数
定义一个递归函数 matchHelper(title, tIndex, parts, pIndex),其中:
title:待匹配的标题。tIndex:当前匹配到标题的索引。parts:解析后的创意部分列表。pIndex:当前匹配到创意的部分索引。
递归终止条件:
- 当所有创意部分都匹配完毕时,检查标题是否也完全匹配。
匹配步骤:
- 如果当前创意部分是固定部分,检查标题当前位置是否匹配。如果匹配,继续递归匹配下一个部分。
- 如果当前创意部分是通配符,尝试匹配标题当前位置的任意长度字符串(包括空字符串),并递归匹配剩余部分。
4. 复杂度分析
- 时间复杂度:最坏情况下为
O(m^n),其中m是标题的长度,n是创意中通配符的数量。但由于题目限制N <= 10和字符串长度<= 100,递归方法是可行的。 - 空间复杂度:
O(m + n),用于递归调用栈和存储解析后的创意部分。
5. 注意
题目代码中提供的示例字符串是有误的,比如False写成了Fales,True写成了Trur。
System.out.println(solution(4, "ad{xyz}cdc{y}f{x}e", testTitles1).equals("True,False,Fales,True"));
算法实现
import java.util.*;
public class Main {
// 定义一个类来表示创意的每一部分
static class Part {
String value;
boolean isWildcard;
Part(String value, boolean isWildcard) {
this.value = value;
this.isWildcard = isWildcard;
}
}
public static List<Part> parseCreative(String creative) {
List<Part> parts = new ArrayList<>();
StringBuilder sb = new StringBuilder();
boolean inWildcard = false;
for (int i = 0; i < creative.length(); i++) {
char c = creative.charAt(i);
if (c == '{') {
// 如果有积累的固定部分,添加到列表
if (sb.length() > 0) {
parts.add(new Part(sb.toString(), false));
sb.setLength(0);
}
inWildcard = true;
} else if (c == '}') {
// 通配符结束,添加到列表
parts.add(new Part(sb.toString(), true));
sb.setLength(0);
inWildcard = false;
} else {
sb.append(c);
}
}
// 添加剩余的固定部分
if (sb.length() > 0) {
parts.add(new Part(sb.toString(), false));
}
return parts;
}
private static boolean matchHelper(String title, int tIndex, List<Part> parts, int pIndex) {
// 如果所有部分都匹配完毕,检查标题是否也完全匹配
if (pIndex == parts.size()) {
return tIndex == title.length();
}
Part currentPart = parts.get(pIndex);
if (!currentPart.isWildcard) {
// 固定部分需要精确匹配
if (title.startsWith(currentPart.value, tIndex)) {
return matchHelper(title, tIndex + currentPart.value.length(), parts, pIndex + 1);
} else {
return false;
}
} else {
// 通配符部分,可以匹配任意长度,包括空字符串
// 尝试所有可能的匹配长度
for (int i = tIndex; i <= title.length(); i++) {
if (matchHelper(title, i, parts, pIndex + 1)) {
return true;
}
}
return false;
}
}
public static String solution(int n, String template, String[] titles) {
List<Part> parts = parseCreative(template);
StringBuilder sb = new StringBuilder();
for(int i=0;i<n;i++){
if(matchHelper(titles[i], 0, parts, 0)){
sb.append("True");
}else{
sb.append("False");
}
if(i == n-1){
break;
}
sb.append(",");
}
// System.out.println(sb.toString());
return sb.toString();
}
public static void main(String[] args) {
// You can add more test cases here
String[] testTitles1 = {"adcdcefdfeffe", "adcdcefdfeff", "dcdcefdfeffe", "adcdcfe"};
String[] testTitles2 = {"CLSomGhcQNvFuzENTAMLCqxBdj", "CLSomNvFuXTASzENTAMLCqxBdj", "CLSomFuXTASzExBdj", "CLSoQNvFuMLCqxBdj", "SovFuXTASzENTAMLCq", "mGhcQNvFuXTASzENTAMLCqx"};
String[] testTitles3 = {"abcdefg", "abefg", "efg"};
// System.out.println(solution(4, "ad{xyz}cdc{y}f{x}e", testTitles1).equals("True,False,Fales,True"));
System.out.println(solution(4, "ad{xyz}cdc{y}f{x}e", testTitles1).equals("True,False,False,True"));
// System.out.println(solution(6, "{xxx}h{cQ}N{vF}u{XTA}S{NTA}MLCq{yyy}", testTitles2).equals("False,False,Fales,False,Fales,True"));
System.out.println(solution(6, "{xxx}h{cQ}N{vF}u{XTA}S{NTA}MLCq{yyy}", testTitles2).equals("False,False,False,False,False,True"));
// System.out.println(solution(3, "a{bdc}efg", testTitles3).equals("True,Trur,Fales"));
System.out.println(solution(3, "a{bdc}efg", testTitles3).equals("True,True,False"));
}
}