题解/算法 {2858. 病毒检测}
@LINK: https://www.acwing.com/problem/content/description/2861/
;
没找到思路还是很难的…
其实没找到思路时 就去想暴力! 也就是, 这个模式串T
我们就暴力的枚举他 对于?
他有4种可能, 对于*
我们把他转换成若干个?
(即?...?
), 此时当然方案数是非常多的;
但是, 因为一共有500个长度为500的字符串 其实并不多, 我们把他构造成Trie树, 也就是 我们一边枚举T
的各种形态 同时还要匹配Trie树 (也就是 我们枚举?, *
的选择 他是必须要符合Trie树的 即是Trie树里的前缀); 这样 他的规模并不大, 因为Trie树 分支只有4个 且只有500个字符串 深度为500;
但是, 以下代码会超时的:
auto Dfs = [&]( auto _dfs, int _ind, ___TrieTree_::Node_ * _cur)->void{
ASSERTcustom_( _cur != nullptr);
auto id = reinterpret_cast<long long>( _cur) * 1003 + _ind;
if( Set.find( id) != Set.end()){ return;}
Set.insert( id);
if( _ind >= N){
if( _cur->Vis == 0){ // `cur`会多次访问, 比如`cur=CTC` `T=?*?`, 此时`T=[CT C ()], [() C TC]...`都是可以的;
ANS += _cur->WholeCount;
}
_cur->Vis = 1;
return;
}
if( T[_ind] == '*'){
_dfs( _dfs, _ind+1, _cur); // skip
FOR_( id, 0, Tr.BRANCHcount-1){
if( Tr.NODEisEXIST( _cur->SonPtr[id])){
_dfs( _dfs, _ind, _cur->SonPtr[id]);
}
}
}
else if( T[_ind] == '?'){
FOR_( id, 0, Tr.BRANCHcount-1){
if( Tr.NODEisEXIST( _cur->SonPtr[id])){
_dfs( _dfs, _ind+1, _cur->SonPtr[id]);
}
}
}
else{
auto nex = _cur->SonPtr[ Tr.GETbranchID( T[_ind])];
if( Tr.NODEisEXIST( nex)){
_dfs( _dfs, _ind+1, nex);
}
}
};
Dfs( Dfs, 0, Tr.RootNode);
cout<< M - ANS;
关键是如何优化超时呢?
先看一个错误的处理: 超时, 主要是由于*
(因为他的可能性太多了), 于是 在if( T[_ind] == '*'){
这里 添加一个if( _cur->Vis == 1){ continue;} _cur->Vis = 1;}
;的优化; 你可能认为, 比如当前是*
cur = "XXX"
那么此时你的*
其实会遍历 Trie里所有以XXX
的前缀, 因此 再遇到cur
就不必遍历了;
.
说白了, 对于Dfs( ind, cur)
, 你只是对cur
进行了记忆化DFS;
其实这样是错误的, 因为还有一个ind
呢, 比如cur = "XXX"
, 而对于不同的ind
其实他产生的效果 是不一样的, 他对应的ind
可能是*, *?*
, 此时虽然cur="XXX"
即对应Trie的同一个节点 但是* 和 *?*
他俩的后缀 和匹配cur
的后缀, 这就不一样了 因为*
的后缀 和 *?*
的后缀是不同的;
因此, 还是要中规中矩, 既然是DFS, 那就对他整个函数参数进行记忆化 即(int, void*)
; 关键是怎么哈希呀? 把void*
转换为int64
? 然后用set< pair<int,int64> >
? (这会超时的);
.
节点个数 经测试 只有2e5个, 即我们要对(500, 2e5)
进行记忆化; 虽然说char [1e8]
是会爆空间的, 但你可以用bitset<1e8>
她不会爆空间;
. .
虽然我们用的是指针节点(不是数组下标), 即现在的问题是 如何把void*
转换为2e5
呢? (千万别改底层代码 就太小题大做了), 你直接在每个节点里 放一个int id
记录他的ID号不就可以啦;
static bitset<200005 * 502> Vis;
auto Dfs = [&]( auto _dfs, int _ind, ___TrieTree_::Node_ * _cur)->void{
int id = _cur->Id * 502 + _ind;
if( Vis[ id]){ return;}
Vis[ id] = 1;
...
};