华为OD机试笔试2024年C卷D卷 - 中文分词模拟器 (java/python/c++)

华为OD机试(C卷+D卷)2024真题目录(Java & c++ & python)

题目描述

给定一个连续不包含空格的字符串,该字符串仅包含英文小写字母及英文标点符号(逗号、分号、句号),同时给定词库,对该字符串进行精确分词。

说明:

  • 精确分词:字符串分词后,不会出现重叠。即"ilovechina",不同词库可分割为"i,love,china",“ilove,china”,不能分割出现重叠的"i,ilove,china",i 出现重叠
  • 标点符号不成词,仅用于断句
  • 词库:根据外部知识库统计出来的常用词汇例:dictionary = [“i”, “love”, “china”, “lovechina”, “ilove”]
  • 分词原则:采用分词顺序优先且最长匹配原则

“ilovechina”,假设分词结果 [i,ilove,lo,love,ch,china,lovechina],则输出 [ilove,china]

错误输出:[i,lovechina],原因:“ilove” > 优先于 “lovechina” 成词

错误输出:[i,love,china],原因:“ilove” > "i"遵循最长匹配原则

输入描述

第一行输入待分词语句 “ilovechina”

  • 字符串长度限制:0 < length < 256

第二行输入中文词库 “i,love,china,ch,na,ve,lo,this,is,this,word”

  • 词库长度限制:1 < length < 100000

输出描述

按顺序输出分词结果 “i,love,china”

用例1

输入

ilovechina
i,love,china,ch,na,ve,lo,this,is,the,word

输出

i,love,china

用例2

输入

iat
i,love,china,ch,na,ve,lo,this,is,the,word,beauti,tiful,ful

输出

i,a,t

说明
单个字母,
不在词库中且不成词则输出单个字母

用例3

输入

ilovechina,thewordisbeautiful
i,love,china,ch,na,ve,lo,this,is,the,word,beauti,tiful,ful

输出

i,love,china,the,word,is,beauti,ful

说明 标点符号为英文标点符号

解题思路

句子:ilovechina

词库:[i,love,china,ch,na,ve,lo,this,is,the,word]

现在要用词库里面的词汇组成句子。并且选择词汇时,优先选择词库中靠前的,且长度最长的。

比如组成句子“ilovechina”的第一个词汇,必然是 “i” 开头的,因此我们去词库中找 “i” 开头的词汇,按词库顺序依次是:

  • i
  • is

其中 is 虽然是 i 开头,但是不符合句子后续部分要求,因此选择词库中词汇 “i”。

此时句子还剩下 “lovechina” 没有分词,则继续在词库中查找 “l” 开头的词汇,按词库顺序依次是:

  • love
  • lo

其中 “love” 是顺序靠前,且长度较长的,因此选择词库中词汇 “love”。

此时句子还剩下 “china” 没有分词,则继续在词库中查找 “c” 开头的词汇,按词库顺序依次是:

  • china
  • ch

其中 “china” 是顺序靠前,且长度较长的,因此选择词库中词汇 “china”。

此时句子"ilovechina" 完成分词,分词结果为:[“i”, “love”, “china”]。

C++、Java、Python代码如下:

C++参考代码

#include <bits/stdc++.h>
using namespace std;

/**
 * 分割输入字符串,返回分割后的子字符串向量
 * @param separator 分隔符集合
 * @return 分割后的子字符串向量
 */
vector<string> splitCin(set<char> &separator) {
    string inputLine;
    getline(cin, inputLine);

    vector<string> result;
    string currentFragment;

    for (const auto &ch : inputLine) {
        if (separator.count(ch) > 0) {
            // 如果遇到分隔符,将当前片段添加到结果中
            if (!currentFragment.empty()) {
                result.emplace_back(currentFragment);
                currentFragment.clear();
            }
        } else {
            // 非分隔符字符加入当前片段
            currentFragment += ch;
        }
    }

    // 添加最后一个片段
    if (!currentFragment.empty()) {
        result.emplace_back(currentFragment);
    }

    return result;
}

int main() {
    // 定义分隔符集合
    set<char> separator({',', '.', ';'});

    // 读取输入并按分隔符分割为多个子字符串
    vector<string> sentences = splitCin(separator);
    vector<string> words = splitCin(separator);

    // 词库,用于记录待匹配的词汇
    set<string> wordSet(words.begin(), words.end());

    // 待分词的句子队列
    deque<string> sentenceQueue(sentences.begin(), sentences.end());

    // 最终的分词结果
    deque<string> finalResult;

    // 逐句处理分词
    while (!sentenceQueue.empty()) {
        string sentence = sentenceQueue.front();
        sentenceQueue.pop_front();

        int substringEndIndex = sentence.length();

        for (; substringEndIndex > 0; substringEndIndex--) {
            // 截取句子的前缀子串
            string fragment = sentence.substr(0, substringEndIndex);

            // 如果子串存在于词库中
            if (wordSet.count(fragment) > 0) {
                // 将子串加入最终结果
                finalResult.emplace_back(fragment);
                // 从词库中移除该子串
                wordSet.erase(fragment);

                // 如果子串仅是句子的一部分,将剩余部分重新加入待处理队列
                if (substringEndIndex < sentence.length()) {
                    sentenceQueue.push_front(sentence.substr(substringEndIndex));
                }
                break;
            }
        }

        // 如果没有找到匹配词汇
        if (substringEndIndex == 0) {
            // 将句子的第一个字符加入最终结果
            finalResult.emplace_back(1, sentence[0]);

            // 将剩余部分重新加入待处理队列
            if (sentence.length() > 1) {
                sentenceQueue.push_front(sentence.substr(1));
            }
        }
    }

    // 输出最终结果,使用逗号分隔
    cout << finalResult[0];
    for (int i = 1; i < finalResult.size(); i++) {
        cout << "," << finalResult[i];
    }
    cout << endl;

    return 0;
}

Java参考代码

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        // 读取输入的句子并按照逗号、句号和分号进行分割
        String[] sentences = sc.nextLine().split("[,.;]");
        // 读取输入的词库并按照逗号、句号和分号进行分割
        String[] words = sc.nextLine().split("[,.;]");

        // 输出分词结果
        System.out.println(getResult(sentences, words));
    }

    /**
     * 根据给定的句子和词库进行分词,并返回分词后的结果字符串
     *
     * @param sentences 需要分词的句子数组
     * @param words     词库数组
     * @return 分词后的结果字符串,词汇间以逗号分隔
     */
    public static String getResult(String[] sentences, String[] words) {
        // 使用HashSet存储词库,便于快速查找和删除
        HashSet<String> wordSet = new HashSet<>(Arrays.asList(words));

        // 使用LinkedList记录待分词的句子,作为队列
        LinkedList<String> queue = new LinkedList<>(Arrays.asList(sentences));

        // 使用LinkedList存储最终的分词结果
        LinkedList<String> ans = new LinkedList<>();

        // 逐句处理队列中的句子,直到队列为空
        while (!queue.isEmpty()) {
            // 从队列中取出第一个句子
            String sentence = queue.removeFirst();

            // 从句子长度开始逐步缩短匹配
            int r = sentence.length();
            for (; r > 0; r--) {
                // 截取句子的前缀子串
                String fragment = sentence.substring(0, r);

                // 如果词库中存在该子串,则进行匹配
                if (wordSet.contains(fragment)) {
                    // 将匹配到的子串添加到结果中
                    ans.addLast(fragment);
                    // 从词库中移除该子串,确保每个词汇只使用一次
                    wordSet.remove(fragment);

                    // 如果子串不是句子的全部,则将剩余部分重新加入队列
                    if (r < sentence.length()) {
                        queue.addFirst(sentence.substring(r));
                    }
                    break;
                }
            }

            // 如果没有找到匹配的子串,则将第一个字符作为独立的词汇处理
            if (r == 0) {
                // 将第一个字符添加到结果中
                ans.add(Character.toString(sentence.charAt(0)));

                // 将剩余部分重新加入队列
                if (sentence.length() > 1) {
                    queue.addFirst(sentence.substring(1));
                }
            }
        }

        // 使用StringJoiner将结果中的词汇用逗号连接起来
        StringJoiner sj = new StringJoiner(",");
        ans.forEach(sj::add);

        // 返回最终的分词结果字符串
        return sj.toString();
    }
}

Python参考代码

import re

# 获取输入并使用正则表达式按照指定分隔符分割为列表
sentences = list(filter(lambda x: x != "", re.split(r"[,.;]", input())))
words = list(filter(lambda x: x != "", re.split(r"[,.;]", input())))

# 使用集合记录词库中的词汇,方便查找
word_set = set(words)
# ans 用于记录最终的分词结果
ans = []

# 处理每一个句子
while len(sentences) > 0:
    # 从句子列表中取出一个句子进行分词处理
    sentence = sentences.pop(0)
    length = len(sentence)

    # 从句子的末尾逐渐减少长度,寻找最长匹配的词
    while length > 0:
        fragment = sentence[:length]  # 提取从开头到当前长度的子串

        # 如果子串在词库中
        if fragment in word_set:
            ans.append(fragment)      # 将子串加入最终结果
            word_set.remove(fragment)  # 从词库中移除已使用的词汇

            # 如果子串只是一部分句子,将剩余部分重新放回待处理的句子列表
            if length < len(sentence):
                sentences.insert(0, sentence[length:])
            break

        length -= 1

    # 如果没有找到匹配的词,则输出句子的第一个字母
    if length == 0:
        ans.append(sentence[0])

        # 将句子的剩余部分放回待处理的句子列表
        if len(sentence) > 1:
            sentences.insert(0, sentence[1:])

# 打印最终的分词结果,使用逗号分隔
print(",".join(ans))

  • 15
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

算法之旅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值