zoukankan      html  css  js  c++  java
  • HDU

    Minimum Index

    题意

    求字符串所有前缀的所有后缀表示中字典序最小的位置集合,最终转换为1112进制表示。比如aab,有三个前缀分别为a,aa,aab。其中a的后缀只有一个a,位置下标1;aa有两个后缀,字典序最小的是a,下标为2;aab有三个后缀,字典序最小的是aab,下标是1。答案为 (1*(1112)^2+2*(1112)^1+1*(1112)^0)

    字符串长度1e6

    分析

    在求字符串的最小表示法中,有一个叫做Lyndon分解的求法,Lyndon分解可以使用Duval算法。详情可以参考 oi-wiki

    (d[j]) 为前缀 j 的字典序最小后缀的起始位置,i, j, k 指针与oi-wiki中介绍的一致。对于下面三种情况讨论d[j]的求解

    (j - k) 为 近似Lyndon串前缀的循环节长度。

    1. (s[j] == s[k]), 那么d[j] = d[k] + (j - k); 本质上是取了 j 所在循环节的开头位置。((s=wwwoverline{w})(overline{w}) 的开头)
    2. (s[j] > s[k]),那么 d[j] = i; 当前(s[i..j]) 是一个Lyndon串,所以d[j] = i;
    3. (s[j] < s[k]),Duval算法中会重新处理 j 所在的这一段((s=wwwoverline{w})(overline{w})), i会被置为这一段的开头,继续后面的分解过程。这里有一个特殊情况需要考虑,如果 (j == k + 1),那么 j 就是下一次分解的开头, i 会被置为 j,所以要手动将d[j] = j。
    #include<bits/stdc++.h>
    
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    #define dbg(x...) do { cout << "33[32;1m" << #x <<" -> "; err(x); } while (0)
    void err() { cout << "33[39;0m" << endl; }
    template<class T, class... Ts> void err(const T& arg,const Ts&... args) { cout << arg << " "; err(args...); }
    const int N = 1e6 + 5;
    const int mod = 1e9 + 7;
    char s[N];
    ll d[N], n;
    
    int main(){
        int T;scanf("%d", &T);
        while(T--){
            scanf("%s", s + 1);
            n = strlen(s + 1);
            int i = 1; d[1] = 1;
            while(i <= n) {
                int j = i + 1, k = i;
                while(j <= n && s[k] <= s[j]) {
                    if(s[k] == s[j]){
                        d[j] = d[k] + (j - k);
                        k ++;
                    }
                    else {
                        d[j] = i;
                        k = i;
                    }
                    j ++;
                }
                d[j] = j; // 当 k == j - 1 时,必须有这一条。因为下面的循环结束后,i = k + 1 也就是 j,接下来的大循环不会在处理当前的 j, 这次 j 是被当做lyndon分解串的一个起点对待的。
                while(i <= k) i += j - k;
            }
            ll res = 0;
            for(int i = n;i>=1; i --){
                res = res * 1112 + d[i];
                res %= mod;
            }
            printf("%lld
    ", res);
        }
        return 0;
    }
    
  • 相关阅读:
    批量修改同一种控件属性
    线程池的使用
    金蝶K3常用数据表
    安装更新
    ApexSQL
    c# 计算一个整型数组的平均
    sqlServer基础知识
    c# 获取字符串数组中最长的的字符串并输出最长的字符串
    c# 获取数组中最大数的值
    c# 计算1-100之间的所有质数(素数)的和
  • 原文地址:https://www.cnblogs.com/1625--H/p/13361162.html
Copyright © 2011-2022 走看看