2020牛客多校第一场反思+总结+题解(AFHIJ)

题目链接t_{i}

https://ac.nowcoder.com/acm/contest/5666#question

题解

A题B-Suffix Array

题意:给你一个字符串s,同时定义B函数为:B\left ( s_{1}s_{2}s_{3}...s_{n}\right )= t_{1}t_{2}t_{3}...t_{n} 其中t_{i}=min_{1\leqslant j< i,s_{j}=s_{i}} \left \{ i-j \right \},如果没有符合的j,那么t_{i}=0。然后让你求出s字符串的所有后缀串按照B函数求出的字符串t排序后的顺序。

这个题有两种做法,一种是直接套用官方题解给出的结论直接求后缀数组就行,一种是按照正常思路来解,但是相对而言还算好想(相比官方题解)。

先讲第二种解法,也是比赛时还能想到的。

首先我们如果能想到一种直接能比较两个后缀大小的方法,那这个题就好办了,直接排序就行了。

下面讲一下怎样比较两个后缀。

考虑一个字符串s按照b函数求出来的新字符串t的形式,一定是开头一个0,然后是一堆数字之后紧跟着一个0,再一堆数字

类似于这种:0x_{1}x_{2}...x_{n}0y_{1}y_{2}...y_{n},因此我们可以考虑先比较两个后缀的开头的连续相同字符个数,不妨定义一个数组dis[i]表示后缀从i开头的连续相同字符个数。

当两个后缀的dis数组不相同时,dis数组小的肯定在前面,eg:aaaab(01110)和aaaba(01102),这时aaaab肯定在aaaba前面。

当两个后缀的dis数组相同时,不妨假设一个后缀为aaabbbab(01101142),另一个后缀为bbbab(01102)。这种情况下我们可以考虑如果能够直接跨过前面0110的比较而直接比较1和2就方便了许多,假设第一个后缀为从i开始,第二个后缀为从j开始,可以得知[i,i+dis[i]]和[j,j+dis[j]]这个区间内的t字符数组都相等,即例子中的"011",而从i+dis[i]开始和j+dis[j]开始的t字符数组里,又有长度为lcp(i+dis[i],j+dis[j])的t字符数组也对应相等,因此我们就可以直接比较b[i+dis[i]+lcp]和b[j+dis[j]+lcp]了,值小的自然在前面。

当然还有两种特殊情况,当i+dis[i]>n时,eg:ba和a,这个时候ba直接放在前面就行了,而当j+dis[j]>n时,放在后面就行了(上一种情况的i和j调换过了的情况)。

时间复杂度:O(nlognlogn)

代码实现:

#include 
using namespace std;
const int N = 2e5+7;
struct SA {
    int sa[N], ra[N], height[N];
    int t1[N], t2[N], c[N];
    void build(int *str, int n, int m) {
        str[n] = 0;
        n++;
        int i, j, p, *x = t1, *y = t2;
        for (i = 0; i < m; i++) c[i] = 0;
        for (i = 0; i < n; i++) c[x[i] = str[i]]++;
        for (i = 1; i < m; i++) c[i] += c[i - 1];
        for (i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i;
        for (j = 1; j <= n; j <<= 1) {
            p = 0;
            for (i = n - j; i < n; i++) y[p++] = i;
            for (i = 0; i < n; i++) if (sa[i] >= j) y[p++] = sa[i] - j;
            for (i = 0; i < m; i++) c[i] = 0;
            for (i = 0; i < n; i++) c[x[y[i]]]++;
            for (i = 1; i < m; i++) c[i] += c[i - 1];
            for (i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
            swap(x, y);
            p = 1;
            x[sa[0]] = 0;
            for (i = 1; i < n; i++)
                x[sa[i]] = (y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + j] == y[sa[i] + j]) ? p - 1 : p++;
            if (p >= n) break;
            m = p;
        }
        n--;
        for (int i = 0; i <= n; i++) ra[sa[i]] = i;
        for (int i = 0, j = 0, k = 0; i <= n; i++) {
            if (k) k--;
            j = sa[ra[i] - 1];
            while (str[i + k] == str[j + k]) k++;
            height[ra[i]] = k;
        }
        st_init(height, n);
    }
    int lg[N], table[23][N];
    void st_init(int *arr, int n) {
        if (!lg[0]) {
            lg[0] = -1;
            for (int i = 1; i < N; i++)
                lg[i] = lg[i / 2] + 1;
        }
        for (int i = 1; i <= n; ++i)
            table[0][i] = arr[i];
        for (int i = 1; i <= lg[n]; ++i)
            for (int j = 1; j <= n; ++j)
                if (j + (1 << i) - 1 <= n)
                    table[i][j] = min(table[i - 1][j], table[i - 1][j + (1 << (i - 1))]);
    }
    int lcp(int l, int r) {
        l = ra[l], r = ra[r];
        if (l > r) swap(l, r);
        ++l;
        int t = lg[r - l + 1];
        return min(table[t][l], table[t][r - (1 << t) + 1]);
    }
}sa;
string s;
int n;
int b[N],dis[N],res[N];
bool cmp(int i,int j){
    if(dis[i]!=dis[j]) return dis[i]n) return  true;
    if(j+dis[j]>n) return false;
    int lcp=sa.lcp(i+dis[i],j+dis[j]);
    // cout<>n>>s){
        int pa=-1,pb=-1;
        for(int i=0;i=0;i--){
            if(s[i]=='a'){
                if(pb==-1) dis[i+1]=n-i;
                else dis[i+1]=pb-i;
                pa=i;
            }
            else{
                if(pa==-1) dis[i+1]=n-i;
                else dis[i+1]=pa-i;
                pb=i;
            }
        }
        // for(int i=0;i

第二种做法(转自2020 牛客多校训练第一场之 A)

定义c[i]的值是i后面离“i指针”对应的字母最近的与s[i]同字符的距离,b数组就与这个c数组的后缀数组。

可以把这个c数组给模拟出来,求出它的后缀(当一个字母后面没有和他相同的字母时,无穷大了,就设为数组长度n,最后一位没啥用,随便设一个比较大的数。避免冲突,就设成n+1)。
根据关系求出sa数组,把这个字符串翻转过来,去掉第一位就能模拟出结果了。

eg:设字符串为abaab,它的c数组为231556,列出c的后缀为: 231556,31556,1556,556,56,6。可以求得 sa:312456,反过来去掉首位的6,54213就是答案了。

代码实现:

#include 
#include 
#include 
#include 
using namespace std;
static const int N = 1e5+7;
string s;
int n;
struct CharRank
{
    int operator[](int i) const { return s[i]; }
    const int *s;
};
template 
struct SA
{
    void compute(int _n, const int *s)
    {
        n = _n;
        sort(n, IdOrder{}, CharRank{s}, sa);
        int range = rank(CharRank{s}, CharRank{s});
        for (int length = 1; length < n && range < n; length <<= 1)
        {
            memcpy(trk, rk, sizeof(*rk) * n);
            TRank r1{n, length, trk}, r0{n, 0, trk};
            sort(range + 1, IdOrder{}, r1, tsa);
            sort(range + 1, TOrder{tsa}, r0, sa);
            range = rank(r0, r1);
        }
    }
 
    int sa[N], rk[N];
 
private:
    struct IdOrder
    {
        int operator[](int i) const { return i; }
    };
 
    struct TOrder
    {
        int operator[](int i) const { return sa[i]; }
        const int *sa;
    };
 
    struct TRank
    {
        int operator[](int i) const
        {
            return i + shift < n ? rk[i + shift] + 1 : 0;
        }
        int n, shift;
        const int *rk;
    };
 
    template 
    void sort(int range, const Order &o, const Rank &r, int *out)
    {
        memset(count, 0, sizeof(*count) * range);
        for (int i = 0; i < n; ++i)
        {
            count[r[i]]++;
        }
        for (int i = 1; i < range; ++i)
        {
            count[i] += count[i - 1];
        }
        for (int i = n; i--;)
        {
            out[--count[r[o[i]]]] = o[i];
        }
    }
 
    template 
    int rank(const Rank0 &r0, const Rank1 &r1)
    {
        rk[sa[0]] = 0;
        for (int _ = 1; _ < n; ++_)
        {
            int i = sa[_];
            int j = sa[_ - 1];
            rk[i] = rk[j] + (r0[i] != r0[j] || r1[i] != r1[j]);
        }
        return rk[sa[n - 1]] + 1;
    }
 
    int n;
    int count[N], tsa[N], trk[N];
};
int c[N + 1];
SA sa;
int main()
{
    while (~scanf("%d", &n))
    {
        cin>>s;
        int pre1=n,pre2=n;
        for(int i=n-1;i>=0;i--){
            if(s[i]=='a'){
                if(pre1==n) c[i]=n-1;
                else c[i]=pre1-i-1;
                pre1=i;
            }
            else{
                if(pre2==n) c[i]=n-1;
                else c[i]=pre2-i-1;
                pre2=i;
            }
        }
        c[n]=n;
        // for (int i = 0; i <= n; i++)
        //     cout << c[i] << " ";
        // cout << endl;
        sa.compute(n+1,c);
        // for (int i = 0; i <= n; i++)
        //     cout << sa.sa[i] << " ";
        // cout << endl;
        for (int i = n-1; i>=1 ; i--)
        {
            printf("%d ", sa.sa[i] + 1);
        }
        printf("%d\n", sa.sa[0] + 1);
    }
    return 0;
}
/*
2
aa
3
aba
5
abaab
 
b 0
ab 00
aab 010
baab 0013
abaab 00213
0<00<0013<00213<010
 
aab ab abaab b baab
010 00 00213 0 0013
 
 
5
abaab
3 2 4 0 0 0
5 4 3 1 0 2
5 4 2 1 3
*/

F题Infinite String Comparision

签到题,不多说,直接上代码

#include
using namespace std;
 
 
int main()
{
    ios::sync_with_stdio(0);
    string a,b;
    while(cin>>a>>b)
    {
        while(a.size()<1e5+10){
            a+=a;
        }
        while(b.size()<1e5+10){
            b+=b;
        }
        int i;
        for(i=0;i<1e5+10;i++){
            if(a[i]>b[i]){
                cout<<'>'< 
   

代码实现:

#include 
#define m_p make_pair
#define p_i pair
#define _for(i, a) for(register int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(register int i = (a), lennn = (b); i <= lennn; ++i)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"
#define mem(a, b) memset(a, b, sizeof(a))
#define mem0(a) memset(a, 0, sizeof(a))
#define fil(a, b) fill(a.begin(), a.end(), b);
#define scl(x) scanf("%lld", &x)
#define sc(x) scanf("%d", &x)
#define abs(x) ((x) > 0 ? (x) : -(x))
#define PI acos(-1)
#define lowbit(x) (x & (-x))
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int maxn = 2000005;
const int maxm = 1000005;
const int maxp = 30;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-8;
const double E = 2.718281828;
 
inline int read() {
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}
 
struct poi {
 
};
 
int n;
LL ji[maxn];
LL in[maxn];
map mp;
 
inline LL quickPow(LL x, LL n, LL mod) {
    LL ans = 1;
    while(n) {
        if(n & 1) ans *= x, ans %= mod;
        x *= x, x %= mod;
        n >>= 1;
    }
    return ans;
}
 
// inline LL inv(LL x) {
//     return quickPow(x, mod - 2, mod);
// }
 
//x->a关于b的逆元
//y->b关于a的逆元
inline void ex_gcd(LL a, LL b, LL &x, LL &y, LL &d){
      if (!b) {d = a, x = 1, y = 0;}
      else{
          ex_gcd(b, a % b, y, x, d);
          y -= x * (a / b);
      }
  }
inline LL inv(LL t){//如果不存在,返回-1
     LL d, x, y;
     ex_gcd(t, mod, x, y, d);
     return d == 1 ? (x % mod + mod) % mod : -1;
 }
 
inline LL C(int n, int m) {
    LL ans = ji[n];
    if(ji[m] < maxn) ans *= in[ji[m]];
    else ans *= inv(ji[m]);
    ans %= mod;
    if(ji[n - m] < maxn) ans *= in[ji[n - m]];
    else ans *= inv(ji[n - m]);
    return ans % mod;
    // return ji[n] * inv(ji[m]) % mod * inv(ji[n - m]) % mod;
}
 
inline void sol() {
    if(mp.count(n)) {
        // printf("%lld\n",  mp[n]);
        cout << mp[n] << "\n";
        return;
    }
    LL ans = 0;
    _for(i, n + 1) {
        ans += C(n, i) * (mod + (i & 1 ? -1 : 1) * in[n + i + 1]) % mod; ans %= mod;
        // ans += mod + ji[n] * inv(ji[i]) % mod * inv(ji[n - i + 1]) % mod * ((i & 1) ? -1 : 1); ans %= mod;
    }
    cout << ans << "\n";
    mp[n] = ans;
}
 
int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    ji[0] = 1;
    _rep(i, 1, maxn - 1) ji[i] = ji[i - 1] * i, ji[i] %= mod;
    _rep(i, 1, maxn - 1) in[i] = inv(i);
    while(cin>>n) {
        LL up=ji[2*n+1]%mod;
        LL down=ji[n]%mod*ji[n]%mod;
        LL ans=up*quickPow(down,mod-2,mod)%mod;
        cout<

 

你可能感兴趣的:(算法学习总结)