简单题 创意标题匹配|豆包MarsCode AI 刷题

88 阅读4分钟

创意标题匹配

题目描述

在广告平台中,为了给广告主一定的自由性和效率,允许广告主在创造标题的时候以通配符的方式进行创意提交。线上服务的时候,会根据用户的搜索词触发的 bidword 对创意中的通配符(通配符是用成对 {} 括起来的字符串,可以包含 0 个或者多个字符)进行替换,用来提升广告投放体验。例如:“{末日血战} 上线送 SSR 英雄,三天集齐无敌阵容!”,会被替换成“帝国时代游戏下载上线送 SSR 英雄,三天集齐无敌阵容!”。给定一个含有通配符的创意和n个标题,判断这句标题是否从该创意替换生成的。

测试样例

样例1:

输入:n = 4, template = "ad{xyz}cdc{y}f{x}e", titles = ["adcdcefdfeffe", "adcdcefdfeff", "dcdcefdfeffe", "adcdcfe"]
输出:"True,False,False,True"

样例2:

输入:n = 3, template = "a{bdc}efg", titles = ["abcdefg", "abefg", "efg"]
输出:"True,True,False"

问题理解

给定一个模板字符串template,其中由固定部分通配符"{}"组成。我们要做的就是将titles数组中的每个标题对应匹配template的组成部分。其中通配符可以代表0个字符或多个字符。

自己的思路分析

刚看到题目时,被这道题搞懵了,平时做的字符串模式匹配想到了kmp,但这道题又有些变幻。我认为难点在:

  • 由于通配符是可以为0的字符,如果用双指针做匹配的话,难以进行调整。比如:样例1的匹配到"ad"的字符成功,但此时template是"{xyz}",而titles[0]的标题此时匹配到"cdc"是在template为固定的部分。此时假如匹配到通配符就跳过就错误了。
  • 由于我是用java做的,还得从头开始写对模板的解析。

正确的解题思路

  1. 模板解析:我们需要将模板解析成固定部分和通配符部分。可以使用正则表达式来识别通配符。
  2. 匹配逻辑:对于每个标题,我们需要尝试将其与模板进行匹配。匹配过程中,我们需要确保标题中的字符能够按照模板的结构进行分配。

代码写法

  1. 我们先将模板template分为parts数组,如["a","{bdc},"efg"],将固定部分和通配分隔。
  2. 我们对每个parts的每个部分进行匹配。
  3. title="abefg",此时先从parts[0]="a"开始匹配,我们运用迭代,设置此时的title的匹配的位置tIndex和parts的匹配的部分... pIndex 匹配流程如下:
    1. pIndex==parts.size(),返回tIndex==title.length()->代表是否匹配成功
    1. 如果匹配到固定部分,则进行part和title部分的匹配->成功,继续跳到title的tIndex+part[0].length()进行匹配,失败返回false
    1. 如果匹配到通配符,需要尝试匹配所有可能的任意长度(相当于跳过i个字符)for (int i = tIndex; i <= title.length(); i++)->此时对于每个结果匹配,当tIndex=i,pIndex=pIndex+1,又回到2的过程.
    1. 最后返回true或者false.

##代码实现


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"));
    }
}

学习总结

这道题虽然是"简单题", 但一点都不简单。考察到了递归和字符串解析以及双指针等知识点。关联的知识点多,需要我们对基础更加熟悉才能有好的思路。后续可能会用python做一次。

共勉!加油!(2024.11.6)