zoukankan      html  css  js  c++  java
  • P1177 【模板】快速排序

    Archie

    本文为倍增做法

    后缀数组题

    后缀数组是啥,把所有的后缀排个序就是后缀数组了

    显然的暴力做法就是全部sort一遍

    这不白瞎

    我们利用倍增的思想,显然可以把一个字符串分成两半进行比较就可以了

    引用一下wiki的图片。

    这里有两个数组

    (SA_i)表示第i小的后缀的编号

    (RK_i)表示第i个后缀的排完序后的编号

    wiki

    然后倍增,就是(O(n log^2n))的做法了。

    但是可以再搞掉一个log,就是把sort换成基数排序

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    const int maxn = 1000010;
    char s[maxn];
    int n, sa[maxn], rk[maxn], oldrk[maxn << 1], id[maxn], cnt[maxn];
    int m, p, w;
    int main() {
        scanf("%s", s + 1);
        n = strlen(s + 1);
        m = max(n, 300);
        for (int i = 1; i <= n; ++i)
            ++cnt[rk[i] = s[i]];
        for (int i = 1; i <= m; ++i)
            cnt[i] += cnt[i - 1];
        for (int i = n; i >= 1; --i)
            sa[cnt[rk[i]]--] = i;
        for (int w = 1; w < n; w <<= 1) {
            memset(cnt, 0, sizeof(cnt));
            for (int i = 1; i <= n; ++i)
                id[i] = sa[i];
            for (int i = 1; i <= n; ++i)
                ++cnt[rk[id[i] + w]];
            for (int i = 1; i <= m; ++i)
                cnt[i] += cnt[i - 1];
            for (int i = n; i >= 1; --i)
                sa[cnt[rk[id[i] + w]]--] = id[i];
            memset(cnt, 0, sizeof(cnt));
            for (int i = 1; i <= n; ++i)
                id[i] = sa[i];
            for (int i = 1; i <= n; ++i)
                ++cnt[rk[id[i]]];
            for (int i = 1; i <= m; ++i)
                cnt[i] += cnt[i - 1];
            for (int i = n; i >= 1; --i)
                sa[cnt[rk[id[i]]]--] = id[i];
            memcpy(oldrk, rk, sizeof(rk));
            int i;
            for (i = 1, p = 0; i <= n; ++i) {
                if (oldrk[sa[i]] == oldrk[sa[i - 1]] &&
                        oldrk[sa[i] + w] == oldrk[sa[i - 1] + w]) {
                    rk[sa[i]] = p;
                } else { //记得去重哦
                    rk[sa[i]] = ++p;
                }
            }
        }
        for (int i = 1; i <= n; ++i)
            printf("%d ", sa[i]);
        return 0;
    }
    

    这样的话还是有点慢,可以进行优化,每一次循环我们算出来的p,事实上就是下一次循环的值域。而且每一次的第二关键字,就是上一次的第一关键字,所以不用再排一遍(rk_{id_i})完全可以存下来,减少不连续内存访问,同理,我们可以吧if换成个cmp

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int maxn=1000010;
    char s[maxn];
    int n,sa[maxn],rk[maxn],oldrk[maxn<<1],id[maxn],cnt[maxn];
    int m,p,w;
    int tem[maxn];
    int i;
    bool cmp(int x, int y, int w) {
      return oldrk[x] == oldrk[y] && oldrk[x + w] == oldrk[y + w];
    }
    int main(){
    	scanf("%s",s+1);
    	n=strlen(s+1);
    	m=max(n,300);
    	for(int i=1;i<=n;++i) 
    		++cnt[rk[i]=s[i]]; 
    	for(int i=1;i<=m;++i)
    		cnt[i]+=cnt[i-1];
    	for(int i=n;i>=1;--i)
    		sa[cnt[rk[i]]--]=i;
    	for(int w=1;w<n;w<<=1,m=p){
    		for (p = 0,i = n; i > n - w; --i) 
    			id[++p] = i;
    		for (i = 1; i <= n; ++i) {
      			if (sa[i] > w) 
    				id[++p] = sa[i] - w;
    		}		
    		memset(cnt,0,sizeof(cnt));
    		for(int i=1;i<=n;++i)
    			++cnt[tem[i]=rk[id[i]]];
    		for(int i=1;i<=m;++i)
    			cnt[i]+=cnt[i-1];
    		for(int i=n;i>=1;--i)
    			sa[cnt[tem[i]]--]=id[i];
    		memcpy(oldrk,rk,sizeof(rk));
    		int i;
    		for( i=1,p=0;i<=n;++i){
    			rk[sa[i]] = cmp(sa[i], sa[i - 1], w) ? p : ++p;
    		}
    		if(p==n){
    			for(int i=1;i<=n;++i){
    				sa[rk[i]]=i;
    			}
    		 break;	 
    		}
    	}
    	for(int i=1;i<=n;++i) 
    		printf("%d ",sa[i]);
    	return 0;
    } 
    
  • 相关阅读:
    python的struct模块
    Linux程序设计学习笔记(独乐乐版)
    理解AndroidX
    Android中的样式和主题
    Android中Fragment的使用
    Android 中Dialog的使用
    直接在apk中添加资源的研究
    Android签名生成和互转
    简单扒一下Volley源码,扩展Volley生命周期
    获取android所有联系人信息
  • 原文地址:https://www.cnblogs.com/For-Miku/p/15036169.html
Copyright © 2011-2022 走看看