POJ 1743 Musical Theme【SAM】

POJ1743 Musical Theme

要找长度\(\ge 5\)且出现次数\(\ge 2\)并且第一次出现和最后一次出现不重叠的最长子串。

题目条件中,如果对于两个串,在一个串的每个数上都加上相同的数之后可以得到另一个串,那么这个两个串可以被是相同的。

首先我们先得到差分数组,然后要求的就是差分数组中长度\(\ge 4\)且出现次数\(\ge 2\)并且第一次出现和最后一次出现不重叠的最长子串

我们需要知道的是每个等价类中终点的最左端和最右端的位置,即(\(firstpos,lastpos\)),每次新加入一个字符所得到的等价类其\(firstpos\)和\(lastpos\)必然为当前的下标,当构造完\(SAM\)之后,由于\(parent\)树的性质,\(link[u]\)所表示的等价类必然是\(u\)所表示的等价类的后缀,所以可以得到:\(lastpos[u] = max_{v\in children} lastpos[v]\)其中\(v\)是\(u\)在\(parent\)树中的儿子,而\(firstpos\)必然一直保持不变。

可以用拓扑排序然后倒序遍历来代替建\(parent\)树然后\(dfs\),拓扑排序即为按\(len\)排序,用基数排序即可

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<string>
#include<algorithm>
#include<stack>
using namespace std;
void ____(){ ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0); }
const int MAXN = 1e5+7;
int n,A[MAXN];
struct SAM{
    int len[MAXN],link[MAXN],firstpos[MAXN],lastpos[MAXN],ch[MAXN][180],tot,last,cnt[MAXN],c[MAXN],sa[MAXN];
    void init(){ link[tot = last = cnt[0] = len[0] = 0] = -1; memset(ch[0],0,sizeof(ch[0])); }
    void extend(int x){
        int np = ++tot, p = last; firstpos[tot] = lastpos[tot] = len[tot] = len[last] + 1;
        memset(ch[tot],0,sizeof(ch[tot])); cnt[tot] = 1;
        while(p!=-1 and !ch[p][x]){
            ch[p][x] = np;
            p = link[p];
        }
        if(p==-1) link[np] = 0;
        else{
            int q = ch[p][x];
            if(len[p]+1==len[q]) link[np] = q;
            else{
                int clone = ++tot;
                cnt[clone] = 0;
                firstpos[clone] = firstpos[q];
                lastpos[clone] = lastpos[q];
                len[clone] = len[p] + 1;
                for(int i = 0; i < 180; i++) ch[clone][i] = ch[q][i];
                link[clone] = link[q];
                while(p!=-1 and ch[p][x]==q){
                    ch[p][x] = clone;
                    p = link[p];
                }
                link[np] = link[q] = clone;
            }
        }
        last = np;
    }
    int solve(){
        for(int i = 0; i <= n; i++) c[i] = 0;
        for(int i = 0; i <= tot; i++) c[len[i]]++;
        for(int i = 1; i <= n; i++) c[i] += c[i-1];
        for(int i = tot; i >= 0; i--) sa[c[len[i]]--] = i;
        int ret = 0;
        for(int i = tot+1; i >= 1; i--){            //这里要注意,基数排序的时候是0~tot,所以排名最后的是tot+1
            int u = sa[i];
            cnt[link[u]] += cnt[u];
            lastpos[link[u]] = max(lastpos[link[u]],lastpos[u]);
            ret = max(ret,min(len[u] + 1,lastpos[u]-firstpos[u]));
        }
        if(ret<5) ret = 0;
        return ret;
    }
}sam;
void solve(){
    for(int i = 1; i <= n; i++) scanf("%d",&A[i]);
    for(int i = 1; i < n; i++) A[i] = A[i+1] - A[i] + 88;
    sam.init(); for(int i = 1; i < n; i++) sam.extend(A[i]);
    printf("%d\n",sam.solve());
}
int main(){
    while(scanf("%d",&n)!=EOF and n) solve();
    return 0;
}

原文地址:https://www.cnblogs.com/kikokiko/p/12700373.html

时间: 2024-10-10 05:23:01

POJ 1743 Musical Theme【SAM】的相关文章

Poj 1743——Musical Theme——————【后缀数组模板题】

Musical Theme Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 22499   Accepted: 7679 Description A musical melody is represented as a sequence of N (1<=N<=20000)notes that are integers in the range 1..88, each representing a key on the

poj 1743 Musical Theme【后缀自动机】

不是很神的一道题,一般. 先差分,最后答案需要+1. 一个right集的len即为该right集的最长相同后缀,考虑到不能重复,所以处理一下该right集的最大与最小的ri,最后答案ans=max(ans,min(r[i]-l[i],len[i])) poj的g++比较恶心,卡空间,卡时间. 用c++交即可. #include<iostream> #include<cstdio> #include<cstdlib> #include<string> #inc

POJ 1743 Musical Theme (后缀数组)

题目大意: 刚才上88个键弹出来的音符. 如果出现重复的,或者是高一个音阶的重复的都算. 思路分析: 具体可以参考训练指南222. height数组表示按照排序后的sa最近的两个后缀的最长前缀. 将height 分块.然后二分答案,二分答案之后去判断是否满足. 要考虑到不重合,还有大于5. 所以二分的时候要从5开始,然后判断的时候要加一个 up - down >len #include <cstdio> #include <iostream> #include <alg

Poj 1743 Musical Theme (后缀数组+二分)

题目链接: Poj  1743 Musical Theme 题目描述: 给出一串数字(数字区间在[1,88]),要在这串数字中找出一个主题,满足: 1:主题长度大于等于5. 2:主题在文本串中重复出现(或者经过调转出现,调转是主题同时加上或者减去同一个整数) 3:重复主题不能重叠 解题思路: 求调转重复出现的子串,那么主题之间的差值一定是不变的.可以求文本串s中相邻两个数的差值,重新组成一个新的文本串S,然后找S后缀串中最长公共不重叠前缀.rank相邻的后缀串,公共前缀一定最长,但是有可能重叠.

poj 1743 Musical Theme(男人八题&amp;后缀数组第一题)

Musical Theme Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 17298   Accepted: 5939 Description A musical melody is represented as a sequence of N (1<=N<=20000)notes that are integers in the range 1..88, each representing a key on the

POJ 1743 Musical Theme(后缀数组+二分答案)

[题目链接] http://poj.org/problem?id=1743 [题目大意] 给出一首曲子的曲谱,上面的音符用不大于88的数字表示, 现在请你确定它主旋律的长度,主旋律指的是出现超过一次, 并且长度不小于5的最长的曲段,主旋律出现的时候并不是完全一样的, 可能经过了升调或者降调,也就是说, 是原来主旋律所包含的数字段同时加上或者减去一个数所得, 当然,两段主旋律之间也是不能有重叠的,现在请你求出这首曲子主旋律的长度, 如果不存在请输出0. [题解] 首先要处理的是升调和降调的问题,由

POJ 1743 Musical Theme 后缀数组 最长重复不相交子串

Musical ThemeTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://poj.org/problem?id=1743 Description A musical melody is represented as a sequence of N (1<=N<=20000)notes that are integers in the range 1..88, each representing a key on the piano. It

poj 1743 Musical Theme(后缀数组)

Musical Theme Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 30544   Accepted: 10208 Description A musical melody is represented as a sequence of N (1<=N<=20000)notes that are integers in the range 1..88, each representing a key on the

POJ 1743 Musical Theme ——后缀数组

[题目分析] 其实找最长的不重叠字串是很容易的,后缀数组+二分可以在nlogn的时间内解决. 但是转调是个棘手的事情. 其实只需要o(* ̄▽ ̄*)ブ差分就可以了. 背板题. [代码] #include <cstdio> #include <cstring> #include <cmath> #include <cstdlib> #include <map> #include <set> #include <queue> #