• 【[POI2006]OKR-Periods of Words】


    很妙的一道题

    感觉又加深了对(KMP)还有(next)数组的理解

    先来看看这个鬼畜的题意,大致就是给你一个字符串,对于这个字符串的每一个前缀,要去找到这个前缀的一个最长的前缀,使得前缀成为这个前缀的前缀倍长之后的前缀

    很蛇皮的题意,之后可能就会懵逼了,这根(KMP)有什么关系

    我们来考虑这样一张图

    tu

    那个标这黑点的部分是这个前缀(i)(next[i]),于是我们如果将这个红色的部分倍长,这个相等的前缀和后缀就可以卡到一起了,于是就满足了前缀成为这个前缀的前缀倍长之后的前缀的要求

    但是这显然不能够满足最长的前缀这个要求

    很显然这个红色的前缀长度为(i-next[i]),如果这个红色的部分更短想让红色部分更长一些的话我们就得让(next[i])变小

    怎么让(next[i])变小呢,很显然我们多跳几次(next)就好了,直到我们跳(next)跳不动了,那么我们就找到了最短的相等前缀和后缀,这个时候红色部分就是最长的了

    所以我们可以写一个暴力跳(next)的代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define re register
    #define maxn 1000005
    #define LL long long
    char S[maxn];
    int nx[maxn];
    int n;
    LL ans;
    int main()
    {
        scanf("%d",&n);
        scanf("%s",S+1);
        nx[0]=nx[1]=0;
        for(re int i=2;i<=n;i++)
        {
            int p=nx[i-1];
            while(p&&S[p+1]!=S[i]) p=nx[p];
            if(S[i]==S[p+1]) nx[i]=p+1;
            else nx[i]=0;
        }
        for(re int i=1;i<=n;i++)
        {
        	int p=nx[i];
        	if(!p) continue;
        	while(nx[p]) p=nx[p];
        	ans+=i-p;
    	}
        printf("%lld",ans);
        return 0;
    }
    
    

    显然暴力跳(next)很容易被卡成(O(N^2)),我们得有一个更妙的方法来得到一个前缀的最短非空相等前缀后缀

    于是我们设(num[i])表示(i)这个前缀的最短相等前缀后缀的长度

    于是答案就是(sum_{i=1}^ni-num[i])

    对于这个(num)数组我们还是可以在求(next)的时候顺便求出来

    如果一个(i)和某个位置(p+1)匹配上了,那么(num[i]=num[p+1])显然(i)最后跳下去也就是(num[p+1])得到的最短相等前缀后缀

    如果没有匹配上(num[i]=i)(num)等于自己

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define re register
    #define maxn 1000005
    #define LL long long
    char S[maxn];
    int nx[maxn],num[maxn];
    int n;
    LL ans;
    int main()
    {
        scanf("%d",&n);
        scanf("%s",S+1);
        nx[0]=nx[1]=0;
        num[1]=1;
        for(re int i=2;i<=n;i++)
        {
            int p=nx[i-1];
            while(p&&S[p+1]!=S[i]) p=nx[p];
            if(S[i]==S[p+1]) nx[i]=p+1,num[i]=num[p+1];
            else nx[i]=0,num[i]=i;
        }
        for(re int i=2;i<=n;i++)
            ans+=(i-num[i]);
        printf("%lld",ans);
        return 0;
    }
    
  • 相关阅读:
    Mac下搭建php开发环境
    phalcon:跟踪sql语句
    phalcon的CLI应用
    phalcon遇到的那些坑
    浏览器 批量大文件上传下载
    网页 批量大文件上传下载
    B/S 批量大文件上传下载
    JavaScript 批量大文件上传下载
    js 批量大文件上传下载
    vue 批量大文件上传下载
  • 原文地址:https://www.cnblogs.com/asuldb/p/10207760.html
Copyright © 2020-2023  润新知