创意标题匹配
问题描述
在广告平台中,为了给广告主一定的自由性和效率,允许广告主在创造标题的时候以通配符的方式进行创意提交。线上服务的时候,会根据用户的搜索词触发的 bidword 对创意中的通配符(通配符是用成对 {} 括起来的字符串,可以包含 0 个或者多个字符)进行替换,用来提升广告投放体验。例如:“{末日血战} 上线送 SSR 英雄,三天集齐无敌阵容!”,会被替换成“帝国时代游戏下载上线送 SSR 英雄,三天集齐无敌阵容!”。给定一个含有通配符的创意和n个标题,判断这句标题是否从该创意替换生成的。
题目地址:www.marscode.cn/practice/9e…
输入输出
输入:n = 4, template = "ad{xyz}cdc{y}f{x}e", titles = ["adcdcefdfeffe", "adcdcefdfeff", "dcdcefdfeffe", "adcdcfe"]
输出:"True,False,False,True"
思路
{xxx}可以看作一个通配符,将模板串看成ad*cdc*f*e,这里*可以匹配任意个,也可以不匹配~ 咱们只考虑一个title
比如说 s :, p: 。 从后往前看,假设 分别指向 的末尾
如果 ,问题变成是否匹配,可以看出这是一个原问题的子问题。
定义 表示是否能够匹配:
-
如果 , 问题变成
-
如果 ,说明这个位置可以匹配任意长度的字符,只要有一个匹配成功就是成功,所以子问题要或 起来。
- 不匹配,问题变成 dfs(i, j - 1)
- 匹配一个,问题变成 dfs(i - 1, j - 1)
- ......
- 都匹配,问题变成 dfs(-1, j - 1)
递归边界, . 由于有很多重复的状态,需要一个数组进行记忆化,定义为, 初始值为-1 .
class Solution {
char[] s, t;
int[][] memo;
int len;
public boolean isMatch(String title, String p) {
s = title.toCharArray();
t = p.toCharArray();
int m = s.length, n = t.length;
for (char c : t) {
if (c != '*') break;
len ++;
}
memo = new int[m][n];
for (int[] row : memo) Arrays.fill(row, -1);
return dfs(m - 1, n - 1);
}
private boolean dfs(int i, int j) {
if (j < 0) return i < 0;
if (i < 0) return j < len;
if (memo[i][j] != -1) return memo[i][j] == 1;
boolean ans = true;
if (t[j] == '*') {
boolean f = false;
for (int k = i; k >= -1; k --) {
f |= dfs(k, j - 1);
}
ans &= f;
} else ans &= (s[i] == t[j]) && dfs(i - 1, j - 1);
memo[i][j] = ans ? 1 : 0;
return ans;
}
}
时间复杂度 ,其中分别是 的长度。
有没有什么优化的地方呢?我们看的部分:
, 那么 等于什么呢?
,
可以发现: ,将 优化到 ,整体时间复杂度变成
class Solution {
char[] s, t;
int[][] memo;
int len;
public boolean isMatch(String title, String p) {
s = title.toCharArray();
t = p.toCharArray();
int m = s.length, n = t.length;
for (char c : t) {
if (c != '*') break;
len ++;
}
memo = new int[m][n];
for (int[] row : memo) Arrays.fill(row, -1);
return dfs(m - 1, n - 1);
}
private boolean dfs(int i, int j) {
if (j < 0) return i < 0;
if (i < 0) return j < len;
if (memo[i][j] != -1) return memo[i][j] == 1;
boolean ans = true;
if (t[j] == '*') {
ans &= dfs(i, j - 1) || dfs(i - 1, j);
}
else ans &= (s[i] == t[j]) && dfs(i - 1, j - 1);
memo[i][j] = ans ? 1 : 0;
return ans;
}
}
递归递归,有递有归,我们可以省区递的部分,只保留归,也就是写成循环的形式。
class Solution {
public boolean isMatch(String st, String p) {
var s = st.toCharArray();
var t = p.toCharArray();
int m = s.length, n = t.length;
int len = 0;
for (char c : t) {
if (c != '*') break;
len ++;
}
boolean[][] dp = new boolean[m + 1][n + 1];
for (int j = 0; j <= len; j ++) dp[0][j] = true;
for (int i = 0; i < m; i ++) {
for (int j = 0; j < n; j ++) {
dp[i + 1][j + 1] = true;
if (t[j] == '*') {
dp[i + 1][j + 1] &= dp[i + 1][j] || dp[i][j + 1];
} else dp[i + 1][j + 1] &= (s[i] == t[j]) && dp[i][j];
}
}
return dp[m][n];
}
}
总体时间复杂度
最终代码:
public class Main {
public static String solution(int n, String template_, String[] titles) {
// Please write your code here
int m = template_.length();
StringBuilder builder = new StringBuilder(m);
for (int i = 0; i < m; i ++) {
if (template_.charAt(i) == '{') {
for (int j = i + 1; j < m; j ++) {
if (template_.charAt(j) == '}') {
i = j;
break;
}
}
builder.append('*');
}
else {
builder.append(template_.charAt(i));
}
}
char[] p = builder.toString().toCharArray();
int len = 0;
for (char c : p) {
if (c != '*') break;
len ++;
}
m = p.length;
var bd = new StringBuilder();
for (int k = 0; k < titles.length; k ++) {
char[] s = titles[k].toCharArray();
int q = s.length;
boolean[][] dp = new boolean[q + 1][m + 1];
for (int j = 0; j <= len; j ++) dp[0][j] = true;
for (int i = 0; i < q; i ++) {
for (int j = 0; j < m; j ++) {
dp[i + 1][j + 1] = true;
if (p[j] == '*') {
dp[i + 1][j + 1] &= dp[i + 1][j] || dp[i][j + 1];
} else {
dp[i + 1][j + 1] &= (p[j] == s[i]) & dp[i][j];
}
}
}
bd.append(dp[q][m] ? "True" : "False");
if (k < titles.length - 1) bd.append(',');
}
return bd.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,False,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,True,False"));
}
}