恢复空格(字典树)

面试题 17.13. 恢复空格

难度中等169

哦,不!你不小心把一个长篇文章中的空格、标点都删掉了,并且大写也弄成了小写。像句子"I reset the computer. It still didn’t boot!"已经变成了"iresetthecomputeritstilldidntboot"。在处理标点符号和大小写之前,你得先把它断成词语。当然了,你有一本厚厚的词典dictionary,不过,有些词没在词典里。假设文章用sentence表示,设计一个算法,把文章断开,要求未识别的字符最少,返回未识别的字符数。

注意:本题相对原题稍作改动,只需返回未识别的字符数

示例:

输入:
dictionary = ["looked","just","like","her","brother"]
sentence = "jesslookedjustliketimherbrother"
输出: 7
解释: 断句后为"jess looked just like tim her brother",共7个未识别字符。

提示:

  • 0 <= len(sentence) <= 1000
  • dictionary中总字符数不超过 150000。
  • 你可以认为dictionarysentence中只包含小写字母。

题解:采用动态规划,创建一个数组 dp[] 用来记录结果。句子从前往后看,其中 dp[0]=0 表示句子是空字符串时没有未识别的字符,dp[i] 表示句子前 i 个字符中最少的未识别字符数。

对于前 i 个字符,即句子字符串的 [0,i),它可能是由最前面的 [0,j)子字符串加上一个字典匹配的单词得到,也就是 dp[i]=dp[j], j<i;也可能没找到字典中的单词,可以用它前 i-1 个字符的结果加上一个没有匹配到的第 i 个字符,即 dp[i]=dp[i-1]+1,每次更新最小的dp[i]。

为了更好的查找是否存在满足的字典,采用字典树结构优化,采用从末尾开始查找匹配到的字典,通过具体详解见:https://leetcode-cn.com/problems/re-space-lcci/solution/hui-fu-kong-ge-by-leetcode-solution/

class Trie {
public:
	Trie* next[26] = { nullptr };	//存储可能的下一个字符(小写字母共26位)
	bool isEnd;
	Trie() { isEnd = 0; }

	// 从末尾建立树
	void insert(string s) {
		Trie* cur = this;
		for (int i = s.length() - 1; i >= 0; --i) {
			int t = s[i] - 'a';
			if (cur->next[t] == nullptr) {
				cur->next[t] = new Trie();
			}
			cur = cur->next[t];
		}
		cur->isEnd = 1;
	}
};
class Solution {
public:
	int respace(vector<string>& dictionary, string sentence) {
		Trie* head = new Trie();
		for (auto s : dictionary)
			head->insert(s);
		vector<int>dp(sentence.size() + 1,INT32_MAX);
		dp[0] = 0;
		for (int i = 1; i <dp.size(); ++i) {
			Trie* cur = head;
			dp[i] = dp[i - 1] + 1;
			for (int j = i - 1; j >= 0; j--) {
				int num = sentence[j] - 'a';
				if (cur->next[num] == nullptr) {
					break;
				}
				else if (cur->next[num]->isEnd) {
					dp[i] = min(dp[i], dp[j]);
				}
                // if(dp[i] == 0)
                //     break;
				cur = cur->next[num];
			}
		}
		return dp[sentence.size()];
	}
};

面试题 17.17. 多次搜索

难度中等21

给定一个较长字符串big和一个包含较短字符串的数组smalls,设计一个方法,根据smalls中的每一个较短字符串,对big进行搜索。输出smalls中的字符串在big里出现的所有位置positions,其中positions[i]smalls[i]出现的所有位置。

示例:

输入:
big = "mississippi"
smalls = ["is","ppi","hi","sis","i","ssippi"]
输出: [[1,4],[8],[],[3],[1,4,7,10],[5]]

提示:

  • 0 <= len(big) <= 1000
  • 0 <= len(smalls[i]) <= 1000
  • smalls的总字符数不会超过 100000。
  • 你可以认为smalls中没有重复字符串。
  • 所有出现的字符均为英文小写字母。
class Trie {
public:
	Trie* next[26] = { nullptr };	//存储可能的下一个字符(小写字母共26位)
	int isEnd;
	Trie() { isEnd = -1; }

	// 从头建立树
	void insert(string s, int pos) {
		Trie* cur = this;
		for (int i = 0; i < s.length(); ++i) {
			int t = s[i] - 'a';
			if (cur->next[t] == nullptr) {
				cur->next[t] = new Trie();
			}
            cur = cur->next[t];
		}
		cur->isEnd = pos;
	}
};

class Solution {
public:
	vector<vector<int>> multiSearch(string big, vector<string>& smalls) {
		vector<vector<int>>res(smalls.size());
        if(big.size() == 0)
            return res;
		Trie* head = new Trie;
		for (int i = 0; i < smalls.size(); ++i) {
			head->insert(smalls[i], i);
		}
        cout << "sad" <<endl;
		for (int i = 0; i < big.size(); ++i) {
			Trie *cur = head;
			for (int j = i; j < big.size(); ++j) {
				int num = big[j] - 'a';
				if (cur->next[num] == nullptr)
					break;
				else if (cur->next[num]->isEnd != -1)
					res[cur->next[num]->isEnd].push_back(i);
				cur = cur->next[num];
			}
		}
		return res;
	}
};

猜你喜欢

转载自blog.csdn.net/Yanpr919/article/details/114262905