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

posted @ 2025-07-15 18:30  zzuxx  阅读(8)  评论(0)    收藏  举报