Musical Themes [USACO]

 

用的就是后缀数组。最近非常烦躁。谁人定我去或留,定我心中的宇宙。

/*
ID: zhangyc1
LANG: C++
TASK: theme
*/
#include <string>
#include <cstring>
#include <cstdlib>
#include <cstdio>
using namespace std;
#define MAXSIZE 5000
#define MAXDIF 176
int N;
int arrNum[MAXSIZE], arrRank[MAXSIZE], arrSufix[MAXSIZE], arrHeight[MAXSIZE];

void prepairData()
{
    int nPre, nCur;
    scanf("%d%d", &N, &nPre);
    for (int i = 0; i < N - 1; i++)
    {
        scanf("%d", &nCur);
        arrNum[i] = nCur - nPre + 88;// arrRank[i] : [1, 175]
        nPre = nCur;
    }
    arrNum[N - 1] = 0;// 补一个唯一的0作为序列结尾
}

inline bool CmpRank(int a, int b, int nStep)
{
    return arrRank[a] == arrRank[b] && arrRank[a + nStep] == arrRank[b + nStep];
}

void DoubleAdding()
{
    int arrCount[MAXSIZE], nMaxVal = MAXDIF;
    int arrRankTemp[MAXSIZE], arrSufix2[MAXSIZE];

    // 首次基数排序
    memcpy(arrRank, arrNum, sizeof(int) * N);
    memset(arrCount, 0, sizeof(arrCount));
    for (int i = 0; i < N; i++)
        arrCount[arrRank[i]]++;
    for (int i = 1; i < nMaxVal; i++)
        arrCount[i] += arrCount[i - 1];
    for (int i = N - 1; i >= 0; i--)
        arrSufix[--arrCount[arrRank[i]]] = i;
    int nRankNum = 0;
    for (int nStep = 1; nRankNum < N; nStep *= 2)
    {
        // 根据上次第一关键字的排序生成本次第二关键字的排序结果
        int nSorted = 0;
        for (int i = N - nStep; i < N; i++)
            arrSufix2[nSorted++] = i;
        for (int i = 0; i < N; i++)
            if (arrSufix[i] >= nStep) arrSufix2[nSorted++] = arrSufix[i] - nStep;

        // 基数排序
        memset(arrCount, 0, sizeof(arrCount));
        for (int i = 0; i < N; i++)
            arrCount[arrRank[i]]++;
        for (int i = 1; i < nMaxVal; i++)
            arrCount[i] += arrCount[i - 1];
        for (int i = N - 1; i >= 0; i--)
            arrSufix[--arrCount[arrRank[arrSufix2[i]]]] = arrSufix2[i];

        // 更新rank
        nRankNum = 1, arrRankTemp[N - 1] = 0;
        for (int i = 1; i < N; i++)
        {
            if (CmpRank(arrSufix[i], arrSufix[i - 1], nStep))
                arrRankTemp[arrSufix[i]] = nRankNum - 1;
            else
                arrRankTemp[arrSufix[i]] = nRankNum++;
        }
        memcpy(arrRank, arrRankTemp, sizeof(int) * N);
        nMaxVal = nRankNum;
    }
}

void CalHeight()
{
    int k = 0;
    for (int i = 0; i < N - 1; i++)
    {
        if (k) k--;
        for (int j = arrSufix[arrRank[i] - 1]; arrNum[j + k] == arrNum[i + k]; k++);
        arrHeight[arrRank[i]] = k;
    }
}

bool GroupHeight(int nStep)
{
    int nSaMax = arrSufix[1], nSaMin = arrSufix[1];
    int i = 2; // 因为arrHeight[1] = 0, 所以从2开始进入第一组
    while (i < N)
    {
        if (arrHeight[i] < nStep)
        {// 因为arrNum数组为后个数减前个数生成的数列。在还原原始序列时是在各元素前插入了个原始值,故sa值相差应大于nStep
         // 如12345 -- 1111  第3位(从0开始)的1其实是原始的4与3相减生成的,第2位的1是原始的3与2相减生成的,这两个串在原始序列中是相交的。
            if (nSaMax - nSaMin > nStep)
                return true;
            nSaMax = arrSufix[i], nSaMin = arrSufix[i];
        }
        else
        {
            if (arrSufix[i] > nSaMax)
                nSaMax = arrSufix[i];
            if (arrSufix[i] < nSaMin)
                nSaMin = arrSufix[i];
        }
        i++;
    }
    return nSaMax - nSaMin > nStep;
}

void process()
{
    DoubleAdding();
    CalHeight();
    int nLeft = 4, nRight = (N - 1) / 2;// 不重复的话,末位的0不可能重复,最长为 (N - 1) / 2
    while (nLeft <= nRight)
    {
        int nHalf = (nLeft + nRight)/2;
        if (GroupHeight(nHalf))
            nLeft = nHalf + 1;
        else
            nRight = nHalf - 1;
    }
    if (nRight >= 4)
        printf("%d\n", nRight + 1);
    else
        printf("0\n");
}

int main(){
    FILE *streamIn = freopen("theme.in","r",stdin);
    FILE *streamOut = freopen("theme.out","w",stdout);
    prepairData();
    process();
    fclose(streamIn);
    fclose(streamOut);
    return 0;
}

 

转载于:https://www.cnblogs.com/doublemystery/archive/2013/05/09/3068675.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值