华为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))