SP694 DISUBSTR - Distinct Substrings - 字典树 + 思维
https://www.luogu.com.cn/problem/SP694
使用字典树的话,最暴力的思路O(n3),肯定会TLE
我们思考,trie的本质
结论:当进行insert操作时,每添加一个节点就代表多了一个本质不同的字符串
接下来模拟解释:
假设原串为AABCD
模拟:初始插入AABCD串
依次创建节点A->A->B->C->D,不就是相当于创建以A为头,分别以A、A、B、C、D为结尾的串,即A、AA、AAB、AABC、AABCD
同理,插入ABCD串
依次创建新节点A->B->C->D,不就是相当于创建以A为头,分别以A、B、C、D为结尾的串吗,但不同的是子串A已经再之前存在过了,所以不再插入
同理,插入BCD串
......
相当于insert操作,不懂可以手动模拟一下,时间复杂度是O(t * |s|2)
ps:这道题用trie的话还有一个坑点就是会卡常,计算时间复杂度比较极限,感觉spoj本来也慢,1s大概1e7-1e8(ai查的),所以不能用ll,改成int就过了
代码
#include<bits/stdc++.h>
#define ll int// 本题时间复杂度很极限,卡longlong
#define endl '\n'
using namespace std;
ll idx;
ll son[1000010][130];
string ss;
ll change(char c)
{
return c;
}
void trie_insert(ll num)
{
ll p = 0;
for(ll i = num; i < ss.size(); i ++)
{
ll u = change(ss[i]);
if(son[p][u] == 0)
{
son[p][u] = idx ++;
}
p = son[p][u];
}
return ;
}
void solve()
{
cin >> ss;
idx = 1;
for(ll i = 0; i < ss.size(); i ++)
{
trie_insert(i);
}
cout << idx - 1 << endl;
for(ll i = 0; i <= idx; i ++)
{
for(ll l = 0; l < 130; l ++)
{
son[i][l] = 0;
}
}
return ;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
ll t = 1;
cin >> t;
while(t --)
{
solve();
}
return 0;
}
我觉得一篇写的不错的字典树文章https://www.luogu.com.cn/article/oufbmjvb

浙公网安备 33010602011771号