luogu P3435 [POI2006]OKR-Periods of Words(kmp求字符串的最大周期,kmp,帮助理解kmp)

题目链接:https://www.luogu.com.cn/problem/P3435

题意:简单的像个一一样,不知道这样的题怎么配坑我那么多时间(从昨天中午开始看,因为考试,大牛客比赛,晚上也没去看,今天早上看了一会,都有一点畏难了,这nm什么破题意)。(当然这题还是很好的,我是直接看的题解,因为这题意确实独步求懂。算是补偿我看了那么久吧,逞能一下口舌之能(狗头报命))。

题意给定一个长度为n(n<=1e6)的字符串s。然后求所有前缀的最大周期的和,注意有以下限制

1.周期不能为本身

2.如果不满足条件的话(即本身前缀函数为0,那么他的最小周期就为自身,一定不满足)周期就为0。所以满足条件的最大周期就是(前缀字符串的长度-最小的前缀==后缀的长度)。

//?注意小技巧,比如pi[10-1]=7,pi[7-1]=3。那么可以直接从pi[10-1]=7,跳到pi[10-1]=3。操作很简单,只需要在循环中每次找到i的最小非0前缀函数的时候将pii-1]更新为这个最小非0前缀函数就好了。相当于路径优化。

总结:更深入的理解了,前缀函数解决字符串的周期问题的操作。收获还是很大的。

一定要注意:p=pi[p-1]才能起到更新的效果,(很多地方都要注意,不只是在求前缀函数的时候需要注意)。不要写成了p=pi[p]emmmmmmmmmm。这差不多,是kmp很常犯的一个错误吧。

#include <bits/stdc++.h>

#define ll long long
#define ld double
#define pi acos(-1)
#define pb push_back
#define mst(a, i) memset(a, i, sizeof(a))
#define pll pair<ll, ll>
#define fi first
#define se second
#define mp(x,y) make_pair(x,y)
#define rep(i,a,n)  for(ll i=a;i<=n;i++)
#define per(i,n,a)  for(ll i=n;i>=a;i--)
#define dbg(x) cout << #x << "===" << x << endl
#define dbgg(l,r,x) for(ll i=l;i<=r;i++) cout<<x[i]<<" ";cout<<endl
using namespace std;

template<class T>void read(T &x){T res=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){res=(res<<3)+(res<<1)+c-'0';c=getchar();}x=res*f;}
inline void print(ll x){if(x<0){putchar('-');x=-x;}if(x>9)print(x/10);putchar(x%10+'0');}
const ll maxn = 1e6 + 10;
const ll mod = 1e9+7;

ll n,a[maxn];
char s[maxn];
//ll inv(ll a){return a==1?1:(ll)(mod-mod/a)*inv(mod%a)%mod;}
//ll gcd(ll a,ll b){return (b==0)?a:gcd(b,a%b);}
//ll qpow(ll a,ll p,ll mod){ll ans=1;a=a%mod;while(p){if(p&1)ans=(ans*a)%mod;p>>=1;a=(a*a)%mod;}return ans;}
int main() {
    ll _s = 1;
    //read(_s);
    //freopen("testdata.in","r",stdin);
	//freopen("testout.out","w",stdout);
    for (ll _=1;_<=_s;_++) {
        read(n);
        scanf("%s",&s);
        a[0]=0;
        rep(i,1,n-1){
            ll t=a[i-1];
            while(t&&s[i]!=s[t]) t=a[t-1];
            t+=(s[i]==s[t]);
            a[i]=t;
        }//?以上求前缀函数的裸操作
        ll ans=0;
        // dbgg(0,n-1,a);
        rep(i,0,n-1){
            if(a[i]==0) continue;
            ll t=i+1;//?最大可能的,然后再递减,如果a[i]即a[t-1]本来就为0,那就加0,否则就一直更新到a[t-1]=0的时候
            while(a[t-1]) t=a[t-1];
            a[i]=t;//?路径优化,每一个i值都对应一个值,所以不需要考虑太多更新之后的路径会有什么影响,会不会更新之后有的路径查不到了。
            //?总之,比如pi[15]=10,pi[10-1]=7,pi[7-1]=3,pi[3-1]=0,那么pi[15],pi[10-1]都可以更新为=3.对结果的正确性wu任何影响
//?注意不加会超时
            // dbg(i+1-t);
            ans+=i+1-t;
        }
        cout<<ans<<endl;
    }
    return 0;
}
/*
input:::

output:::

*/

代码:

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值