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;
    } 
    
  • 相关阅读:
    86. Partition List
    328. Odd Even Linked List
    19. Remove Nth Node From End of List(移除倒数第N的结点, 快慢指针)
    24. Swap Nodes in Pairs
    2. Add Two Numbers(2个链表相加)
    92. Reverse Linked List II(链表部分反转)
    109. Convert Sorted List to Binary Search Tree
    138. Copy List with Random Pointer
    为Unity的新版ugui的Prefab生成预览图
    ArcEngine生成矩形缓冲区
  • 原文地址:https://www.cnblogs.com/For-Miku/p/15036169.html
Copyright © 2011-2022 走看看