一、Occurrences After Bigram
这题比较简答。就是给你一个文本类似的字符串,再给你两个单词,要你找出文本中连续出现给定的两个单词后的第三个单词(可能有多个)。
因为文本字符串中的单词是以空格分割的,恰好前段时间写python代码时觉得python在处理字符串分割这块很方便,所以我直接用python写这题。
class Solution:
def findOcurrences(self, text: str, first: str, second: str) -> List[str]:
textlist=text.split()
textlen=len(textlist)
ans=[]
for ind in range(textlen):
if textlist[ind] == second:
if ind>0 and ind<textlen-1 and textlist[ind-1]==first:
ans.append(textlist[ind+1])
return ans
二、Letter Tile Possibilities
一开始看到这道题,我想到的是子集合+全排列,所以我采用这个思路做题。
首先对字符串排序,然后依次取长度为1到长度为原字符串长度的全排列。
class Solution {
public:
int numTilePossibilities(string tiles) {
int maxlen=tiles.size(),ans=0;
sort(tiles.begin(),tiles.end());
for(int len=1;len<=tiles.size();++len)
permute(ans,tiles,0,tiles.size(),len,0);
return ans;
}
void permute(int& ans,string str,int left,int right,int len,int cur)
{
if(cur==len)
++ans;
else
for(int i=left;i<right;++i)
{
if(i>left&&str[i]==str[left])
continue;
swap(str[left],str[i]);
permute(ans,str,left+1,right,len,cur+1);
}
}
};
三、Insufficient Nodes in Root to Leaf Paths
这道题比赛期间测试集出错了,很多人都卡在第111个样例那里。我用是的DFS暴力求解,变量所有路径,记录下来然后再依次删除,没有什么超前的思维,所以效率较低。
原笔记:
这道题比赛期间通过率只有18%,赛后反馈也不好,不知为何。但我个人觉得这题还是挺不错的,虽然我没做对。
我的思路是既然一个节点必须是所有经过它的根到叶子的路径的和不超过限制才被称为insufficient node,所以一旦有一条路径超过了Limit那我立即可以判断该路径上的所有节点都不是insufficient node。采用这种思想我遍历了所有根到叶子节点的路径,代码写得比较冗长,最后卡在后4个数据集,赛后我看到discuss区域也有人跟我一样,不知道为什么只通过了前111个测试数据,难道有什么未考虑的特殊情形?希望懂的好心人能帮我解答一下,下面先附上我错的那个代码,然后再来说说大神的解法。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
//1:遍历所有路径,标记哪些节点需要被删除;
//2:遍历整棵树,删除标记的节点;
TreeNode* sufficientSubset(TreeNode* root, int limit) {
unordered_map<TreeNode*,bool> is;
vector<TreeNode*> path;
path.push_back(root);
pathsum(root,0,limit,path,is);
deletenode(root,is);
return root;
}
void pathsum(TreeNode* &root,int sum,int limit,vector<TreeNode*>& path,unordered_map<TreeNode*,bool>& is)
{
if(root)
{
if(!is.count(root))
is[root]=false;
if(!root->left&&!root->right)
{
if(sum+root->val>=limit)
{
is[root]=true;
for(TreeNode* node:path)
is[node]=true;
}
}
if(root->left)
{
path.push_back(root->left);
pathsum(root->left,sum+root->val,limit,path,is);
path.pop_back();
}
if(root->right)
{
path.push_back(root->right);
pathsum(root->right,sum+root->val,limit,path,is);
path.pop_back();
}
}
}
void deletenode(TreeNode* &root,unordered_map<TreeNode*,bool>& is)
{
if(root)
{
deletenode(root->left,is);
deletenode(root->right,is);
if(is[root]==false)
root=NULL;
}
}
};
大神的解法:
1、首先采用root->left==root->right的方式确定叶子节点,这是我之前没意识到的;
2、采用limit依次减去节点值的形式,给每个当前遍历到的节点限定了一个需要到达的最小值。若当前节点是叶子节点且其值不超过限制则立即可以判断该节点需要删除,将其置为NULL。
3、如果一个中间节点的子节点都被删除,即都被置为NULL,则其变成叶子节点,所以判断方式不变。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* sufficientSubset(TreeNode* root, int limit) {
if (root->left == root->right)
return root->val < limit ? NULL : root;
if (root->left!=NULL)
root->left = sufficientSubset(root->left, limit - root->val);
if (root->right!=NULL)
root->right = sufficientSubset(root->right, limit - root->val);
return root->left == root->right ? NULL : root;
}
};
四、Smallest Subsequence of Distinct Characters
一个只包含小写字母的字符串,求该字符串包含所有出现过的字母的字典序最小的子串(确保每个字母只出现一次)。
学习了大神的最优思路:
1、计数出现过的字母;
2、由于要求的是子序列,所以遍历字符串,判断当前字符是否可以加入答案字符串;
3、判断方式:如果当前字符的字典序小于当前答案字符串中最后一个字符,且当前答案字符串中最后一个字符在未遍历到的字符中仍会出现的话,就修改答案字符串,将字典序大于当前字符的字符都从答案字符串中删除,而把当前字符加入答案字符串。
class Solution {
public:
string smallestSubsequence(string text) {
vector<int> cnt(26,0);
vector<bool> have(26,false);
for(char ch:text)
++cnt[ch-'a'];
string ans="#";//采用这种方式巧妙避免了下面遍历过程中对ans是否为空的判断
for(char ch:text)
{
int ind=ch-'a';
--cnt[ind];
if(have[ind])
continue;
while(ans.back()>ch&&cnt[ans.back()-'a'])
have[ans.back()-'a']=false,ans.pop_back();
ans+=ch;
have[ch-'a']=true;
}
return ans.substr(1);
}
};