题目描述
有一个很长的由小写字母组成字符串。为了便于对这个字符串进行分析,需要将它划分成若干个部分,每个部分称为一个单词。
出于减少分析量的目的,我们希望划分出的单词数越少越好。你就是来完成这一划分工作的。
输入格式
第一行为一整数 T,表示有 T 组测试数据。
每组测试数据第一行为一字符串。(长度小于 256)
第二行为一整数 N。(1<=N<=100)
以下 N 行,每行一个单词,每个单词长度小于 128。
输出格式
每组测试数据占一行 , 这一行只有一个整数,表示字符串可以被划分成的最少的单词数。我们保证单词是可划分的。
样例
input
1 realityour 5 real reality it your ouroutput
2
思路:遍历字符串,寻找以当前字母结尾时的子串所能拆分的最少单词数,转移方程:dp[i] = min(dp[i], dp[i-word.length()]+1) ,注意处理各种边界条件。实现的过程中用map建立了一个哈希表,将每个单词的最后一个字母作为索引,将所有单词整理成wordlist方便查找。代码实现如下:
#include <iostream>
#include <string>
#include <map>
#include <vector>
using namespace std;
int main()
{
int T;
cin>>T;
for(int t=0; t<T; ++t){
int dp[300] = {0};
int w_cnt;
string st;
map<char, vector<string> > wordhash;//哈希表
cin>>st>>w_cnt;
for(int i=0; i<w_cnt; ++i){
string w;
char key;
cin>>w;
key = w[w.length()-1];//单词的最后一个字母作为索引
/*建哈希表*/
if(wordhash.count(key) == 0){
vector<string> wordlist;
wordlist.push_back(w);
wordhash.insert({key, wordlist});
}
else{
map<char, vector<string> >::iterator it = wordhash.find(key);
it->second.push_back(w);
}
/*建哈希表*/
}
for(int i=0; i<st.length(); ++i){//遍历字符串
char &key = st[i];//获取当前字母
if(wordhash.count(key) == 0){//若不存在当前字母结尾的单词
dp[i] = i>0 ? dp[i-1] : 0;//则能拆分的单词数不变
}
else{
vector<string> &wordlist = wordhash[key];
for(int j=0; j<wordlist.size(); ++j){//遍历以该字母结尾的单词的wordlist
string &word = wordlist[j];
if(i+1>=word.length()){
string sub = st.substr(i+1-word.length(),word.length());
if(word == sub){//若当前单词匹配当前子串
if(i<word.length())//若该子串在句首
dp[i] = 1;
else{//否则更新为最少拆分的情况
dp[i] = dp[i]==0 ? dp[i-word.length()]+1 : min(dp[i], dp[i-word.length()]+1);
}
}
}
}
}
}
cout<<dp[st.length()-1]<<endl;
}
return 0;
}