字典树使用

212. 单词搜索 II

难度困难468

给定一个 m x n 二维字符网格 board 和一个单词(字符串)列表 words,找出所有同时在二维网格和字典中出现的单词。

单词必须按照字母顺序,通过 相邻的单元格 内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。

示例 1:

输入:board = [["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]], words = ["oath","pea","eat","rain"]
输出:["eat","oath"]

示例 2:

输入:board = [["a","b"],["c","d"]], words = ["abcb"]
输出:[]

提示:

  • m == board.length
  • n == board[i].length
  • 1 <= m, n <= 12
  • board[i][j] 是一个小写英文字母
  • 1 <= words.length <= 3 * 104
  • 1 <= words[i].length <= 10
  • words[i] 由小写英文字母组成
  • words 中的所有字符串互不相同
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:
	bool flag[13][13] = { 0 };
	int dir_x[4] = { -1,0,0,1 };
	int dir_y[4] = { 0,1,-1,0 };
	int row, col;
	string mid;
	unordered_set<string>res;
	void dfs(vector<vector<char>>& board, int x, int y, Trie *cur) {
		if (cur->isEnd != -1)
			res.insert(mid);
        //cout << x << ' ' << y <<endl;
		for (int i = 0; i < 4; i++)
		{
			int xx = x + dir_x[i];
			int yy = y + dir_y[i];
			if (xx >= 0 && xx < row &&  yy >= 0 && yy < col && flag[xx][yy] == 0) {
				char s = board[xx][yy];
				if (cur->next[s - 'a'] != nullptr) {
					flag[xx][yy] = 1;
					mid.push_back(s);
					dfs(board, xx, yy, cur->next[s - 'a']);
					flag[xx][yy] = 0;
					mid.pop_back();
				}
			}
		}

	}
	vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
		Trie *Start = new Trie();
        row = board.size();
        col = board[0].size();
		for (auto s : words) {
			Start->insert(s, 1);
		}
		for (int i = 0; i < board.size(); ++i) {
			for (int j = 0; j < board[0].size(); ++j) {
				char s = board[i][j];
				if (Start->next[s - 'a'] != nullptr) {
					flag[i][j] = 1;
					mid.push_back(s);
					dfs(board, i, j, Start->next[s - 'a']);
					flag[i][j] = 0;
					mid.pop_back();
				}
			}
		}
		vector<string>fin;
		for (auto ss : res) {
			fin.push_back(ss);
		}
		return fin;
	}
};

面试题 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]。

为了更好的查找是否存在满足的字典,采用字典树结构优化,采用从末尾开始查找匹配到的字典,通过具体详解见:力扣

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;
	}
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值