题解/算法 {3093. 最长公共后缀查询}
@LINK: https://leetcode.cn/problems/longest-common-suffix-queries/
;
看一个超内存的做法:
把S
中的所有字符串的所有后缀, 都放到一个map< string, pair<int,int> >
里面 (pair
记录{字符串最短长度, 字符串下标}
); 然后查询时 也是遍历当前要查询的字符串的所有后缀 去map
里面查询;
一共有多少个后缀呢? 注意不是1e4 * 5e3
, 因为题目说了*wordsContainer[i].length
的和至多为 5 * 1e5
, 也就是 一共最多是5e5
个后缀, 可是 再加上每个后缀的长度*, 他是5e5 * 5e5
级别, 会爆空间的;
看一个正确的做法(但不是最优解):
既然爆空间了, 那对字符串进行哈希 变成一个整数, 都放到map< int, pair<int,int> >
里面, 此时就不会爆空间了;
但会发生哈希冲突, 因为字符串太多了, 因此 需要用大哈希(即将1e9+7
改为1e18+7
);
vector<int> stringIndices(vector<string>& S, vector<string>& Q) {
___HashString::___Calc_SubStringsHash Calc;
unordered_map< ___HashString::__HashModularType_::__UnsignedModType_, pair<int,int> > MP;
FOR_( i, 0, S.size()-1){
int len = S[i].size();
if( (MP.find(0)==MP.end()) || (len<MP[0].first)){ MP[0] = {len, i};} // 空字符串;
Calc.Initialize( S[i].data(), len);
FOR_( t, 1, len){
auto cur = Calc.Get_hash_subString( t-1, len-1).Value;
if( (MP.find(cur)==MP.end()) || (len<MP[cur].first)){ MP[ cur] = {len, i};}
}
}
vector<int> ANS( Q.size(), -1);
FOR_( q, 0, ANS.size()-1){
int len = Q[q].size();
Calc.Initialize( Q[q].data(), len);
FOR_( t, 1, len){
auto cur = Calc.Get_hash_subString( t-1, len-1).Value;
if( MP.find( cur) != MP.end()){ ANS[q] = MP[cur].second; break;}
}
if( ANS[q] == -1){
ANS[q] = MP[0].second; // 空字符串
}
}
return ANS;
}
最优解: 字典树Trie;
将S中所有字符串 逆序的 放到Trie里面, 然后每个节点记录pair<int,int>
信息;
查询时 也是将要查询的字符串 逆序处理后, 放到Trie里查询;
vector<int> stringIndices(vector<string>& S, vector<string>& Q){
static ___TrieTree Tr;
Tr.Initialize();
FOR_( i, 0, S.size()-1){
auto & s = S[i];
std::reverse( s.begin(), s.end());
Tr.Insert( s.data(), s.size(), {s.size(), i});
}
vector<int> ANS(Q.size());
FOR_( i, 0, Q.size()-1){
auto & s = Q[i];
std::reverse( s.begin(), s.end());
ANS[i] = Tr.Query( s.data(), s.size());
}
return ANS;
}