偶然看到的一道题,一下子把我带回了那个遥远的记忆。觉得这道题跟以前在游戏里做的敏感词过滤有些相似,就随手记录下来。
单词切割题
题目为:
给定一个字符串S和有效单词的字典D,请确定可以插入到S中的最小空格数,使得最终的字符串完全由D中的有效单词组成,并输出解。
如果没有解则应该输出n/a
例如
输入
S = “ilikealibaba”
D = [“i”, “like”, “ali”, “liba”, “baba”, “alibaba”]
Example Output:
输出
“i like alibaba”
解释:
字符串S可能被字典D这样拆分
“i like ali baba”
“i like alibaba”
很显然,第二个查分结果是空格数最少的解。
#include "stdafx.h"
#include <iostream>
#include <string.h>
#include <vector>
#include <map>
#include <assert.h>
//tdw
using namespace std;
struct SNode
{
char letter;//该节点,字母
vector<SNode*> vpChild;//子节点
SNode* pFather;//父节点,回溯的时候用到
bool btrun;//是否截断,例如 “like”,"li" 在"i"时应该截断
SNode(char _letter)
:letter(_letter)
, btrun(false)
{
pFather = nullptr;
vpChild.clear();
}
~SNode()
{
this->letter = ' ';
this->pFather = nullptr;
btrun = false;
for (int i = 0; i < vpChild.size();++i)
{
delete vpChild[i];
vpChild[i] = nullptr;
}
vpChild.clear();
}
SNode(string _s, int _pos = 0)
{//构建一棵树
assert(_s.length() > 0);
assert(_s.length() > _pos);
letter = _s[_pos];
btrun = false;
pFather = nullptr;
SNode* pnode = this;//根节点
for (int i = _pos + 1; i < _s.length(); ++i)
{
SNode* pchild = new SNode(_s[i]);
pchild->pFather = pnode;
pnode->vpChild.push_back(pchild);
pnode = pchild;
}
pnode->btrun = true;//最后一个叶子应该截断
}
SNode* FindChildLetterNode(char _letter)
{
for (SNode* pnode : vpChild)
{
if (pnode->letter == _letter)
return pnode;
}
return nullptr;
}
int Location(string _s, int _beg)
{
assert(_s.length() > 0);
assert(_s.length() > _beg);
assert(_s[_beg] == this->letter);
SNode* pnode = this;
int pos = _beg;
bool bfind = false;//是否找到下一个字符
for (int i = _beg + 1; i < _s.length(); ++i)
{
bfind = false;
for (SNode* p : pnode->vpChild)
{
if (p->letter == _s[i])
{
pnode = p;
++pos;
bfind = true;
break;
}
}
if (!bfind)
{
break;
}
}
if (pnode->btrun)
return pos;
while (true)
{//没有截断,说明出现了存在特殊的字符,需要回溯 例如:字符串里有"li" 有效字典里有"like" "l" "i"
--pos;
pnode = pnode->pFather;
if ((pnode == nullptr) || (pnode == this && !pnode->btrun))
return -1;//出现了不存在的字符,没有截断
if (pnode->btrun)
break;//找到合理的位置
}
return pos;
}
};
int main()
{
string s = "ilikealibaba";
string d[] = { "i","like","ali","liba","baba","alibaba" };
map<char, SNode*> mapLN;//key-first letter,val- node pointer
for (string temp : d)
{
if (temp.length() == 0)
continue;
char firstletter = temp[0];
map<char, SNode*>::iterator iter = mapLN.find(firstletter);
if (iter == mapLN.end())
{//第一次创建firstletter的树
SNode* pnode = new SNode(temp);
mapLN.insert(make_pair(firstletter, pnode));
}
else
{//已存在firstletter树,接下来找到应该插入的位置
SNode* pnode = iter->second;//根节点
SNode* pchild = nullptr;
for (int i = 1; i < temp.length(); ++i)
{
pchild = pnode->FindChildLetterNode(temp[i]);
if (pchild == nullptr)
{
SNode* p = new SNode(temp, i);
p->pFather = pnode;
pnode->vpChild.push_back(p);
break;
}
pnode = pchild;
}
if (pchild != nullptr)
pchild->btrun = true;
}
}
for (int i = 0; i < s.length();)
{
map<char, SNode*>::iterator iter = mapLN.find(s[i]);
if (iter == mapLN.end())
{
cout << "n/a"; //没有找到
break;
}
SNode* pnode = iter->second;
int pos = pnode->Location(s, i);
if (pos < 0)
{
cout << "n/a"; //异常数据
break;
}
while (i <= pos)
{//i = pos+1;
cout << s[i++];
}
cout << " ";//最后还有一个空格可删除
}
map<char, SNode*>::iterator iter = mapLN.begin();
for (; iter != mapLN.end();++iter)
{
delete iter->second;
iter->second = nullptr;
}
mapLN.clear();
return 0;
}