zoukankan      html  css  js  c++  java
  • 洛谷P6140&P2870 [USACO07NOV]Best Cow Line S

    题意

    传送门

    给定一个字符串,每次珂以取出当前字符串的头或者尾,将其加入答案串中(放到最后面),要求必须把该字符串取完,求字典序最小的方案

    分析

    SA简单题

    题目要求的是求字典序最小,很容易想到SA来求

    我们观察一下,对于每一位可能的情况就只有两种,所以我们想到贪心地取,每次取当前头和尾当中较小的那个字符

    但是这样会出现一个问题:如果头和尾相同该怎么办?

    比如这样的一个字符串:BCAB

    现在我们取这个串就不知道该取头还是尾,这个时候如果我们贸然取头的话(变成CAB),我们下一步只能取到C或B(当前就应该取B)

    这样干之后我们会取到BB作为当前已经取了的,实际上我们有更优的选择,那就是第一次取的时候取尾,然后我们珂以取到BA这个串,它的字典序更小

    那么我们考虑在这种情况下怎么搞。

    珂以发现,其实每次取都是在查看当前状态下,是当前串的字典序小,还是当前反串的字典序小,那么这就相当于是比较两个字符串的字典序谁大谁小的问题

    很容易想到后缀数组SA来做。

    具体就是设原串为A,设A的反串是~A,那么此时我们考虑拼接这样的一个字符串 B=A+(分隔符)+ ~A

    此时我们对B串跑一边SA就可以得到原串和反串字典序排序之后的相对大小了(rk数组)

    每次选字符的时候(O(1))比较一下就行了

    代码

    #include<bits/stdc++.h>
    using namespace std;
    template <typename T>
    inline void read(T &x){
    	x=0;char ch=getchar();bool f=false;
    	while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
    	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    	x=f?-x:x;
    	return ;
    }
    template <typename T,typename... Args> inline void read(T& t, Args&... args){read(t);read(args...);}
    template <typename T>
    inline void write(T x){
    	if(x<0) putchar('-'),x=-x;
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    	return ;
    }
    template <typename T>
    inline void print(T x){write(x),putchar(' ');}
    #define ll long long
    #define ull unsigned long long
    #define inc(x,y) (x+y>=MOD?x+y-MOD:x+y)
    #define dec(x,y) (x-y<0?x-y+MOD:x-y)
    #define min(x,y) (x<y?x:y)
    #define max(x,y) (x>y?x:y)
    const int N=1e6+5,M=1e6+5,MOD=1e9+7;
    char str[N];
    int n,m,k,sa[N],rk[N],oldrk[N],id[N],px[N],cnt[N];
    bool cmp(int x,int y,int w){return oldrk[x]==oldrk[y] && oldrk[x+w]==oldrk[y+w];}
    void SA(){
    	int i,p=0,w;
    	m=300;
    	for(i=1;i<=n;i++) ++cnt[rk[i]=str[i]];
    	for(i=1;i<=m;i++) cnt[i]+=cnt[i-1];
    	for(i=n;i>=1;i--) sa[cnt[rk[i]]--]=i;
    	for(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(i=1;i<=n;i++) ++cnt[px[i]=rk[id[i]]];
    		for(i=1;i<=m;i++) cnt[i]+=cnt[i-1];
    		for(i=n;i>=1;i--) sa[cnt[px[i]]--]=id[i];
    		memcpy(oldrk,rk,sizeof(rk));
    		for(p=0,i=1;i<=n;i++) rk[sa[i]]=cmp(sa[i],sa[i-1],w) ? p : ++p;
    		
    	}
    	return ;
    }
    int main(){
    	read(k);char ch;
    	for(int i=1;i<=k;i++) while(isalpha(ch=getchar())) str[i]=ch;
    	n=k<<1|1;
    	for(int i=1;i<=k;i++) str[i+k+1]=str[k-i+1];
    	SA();
    	int l=1,r=k,tot=0;
    	while(l<=r){
    		if(rk[l]<rk[n+1-r]) putchar(str[l++]);
    		else putchar(str[r--]);
    		if((++tot)%80==0) puts("");
    	}
    	system("Pause");
    	return 0;
    }
    
  • 相关阅读:
    114.114.114.114和8.8.8.8
    一台电脑双网卡同时上网
    eNSP模拟器
    路由器UPnP
    子网掩码
    网线水晶头制作
    AP (无线访问接入点(WirelessAccessPoint))
    筛选键
    注册表方法修改网络名称
    图片素材网址
  • 原文地址:https://www.cnblogs.com/Akmaey/p/14206961.html
Copyright © 2011-2022 走看看