某题

后缀数组

题目描述

给定一个字符串S,它的长为n,后缀数组的功能是,将其所有后缀按字典序从小到大排好序。我们对其做一点小小的改动:再给定一个数字m,记ssi表示从S的第i位开始、长度最多为m的子串,我们希望将这些字符串{ssi}按字典序从小到大排序。举个栗子,当S="abcab",m=2时,ssi的值分别为:
ss1="ab"
ss2="bc"
ss3="ca"
ss4="ab"
ss5="b"
但是,只是把{ssi}全部排好序还是太简单了。初始状态下,ss1~ssn按顺序排成一行,我们只能通过不断交换某两个相邻字符串的位置来做排序。再举个栗子,把上面提到的ss1~ss5排好序的一种方案是:

(0)原序列:"ab", "bc", "ca", "ab", "b"
(1)交换第3和第4个串:"ab", "bc", "ab", ca", "b"
(2)交换第2和第3个串:"ab", "ab", "bc", ca", "b"
(3)交换第4和第5个串:"ab", "ab", "bc", b", "ca"
(4)交换第3和第4个串:"ab", "ab", "b", bc", "ca"
现在,你需要求出,最少通过多少次相邻字符串交换,才能把所有子串{ssi}排成字典序从小到大的形式。

( ´_ゝ`)NOIP怎么可能会考后缀数组

输入格式

第一行包含两个整数n和m;
第二行包含字符串S,它的长为n,只包含小写字母。

输出格式

一个整数,表示最少交换次数。

样例输入

5 2
abcab

样例输出

4

样例解释

样例就是题目描述中提到的例子。

数据范围

对于20%的数据,有n≤10;
对于40%的数据,有n≤100;
对于60%的数据,有n≤5000;
另有10%的数据,有m≤5;
另有10%的数据,S是随机生成的;
对于100%的数据,有1≤m≤n≤50000

这是某道考试题
考场上写的是乱搞\((O(nlog(n)))\)一个类似平衡树(权值树状数组或权值线段树)的做法
但是因为某STL容器的巨大常数和存储字符串所需的巨大空间
导致TLE(MLE)
然后正解hash二分字符串判等和归并排序
所以不会出现MLE这种问题

然后我就想,存储字符串用Trie树不就好了,而且需要动态开点
否则会MLE的更惨
然后我就YY了一个做法
然后就写完了
然后还是TLE和MLE……
其实TLE是很好解决的,在每个节点开一棵线段树或者平衡树维护一下就好
但是MLE好像不太好解决
因为每个节点都有26个子节点
空间开销非常大
虽然由DAT这种特殊的"Trie"树
每个节点只需要开2个值维护
但是如何维护子节点的信息是个难题(TLE)
而且问题是我不会用啊23333

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cstdio>
#define Max 26
using namespace std;

struct TrieNode{
    int ti;
    TrieNode *next[Max];
    TrieNode(){
        ti=0;
        memset(next,NULL,sizeof(next));
    }
};

void Insert(TrieNode *root,char * str){
    int len=strlen(str);
    TrieNode *p=root;
    for(int i=0;i<len;++i){
        int id=str[i]-'a';
        if(p->next[id]==NULL)
            p->next[id]=new TrieNode();
        p=p->next[id];
        p->ti++;
    }
}

int searchTrie(TrieNode *root,char *str){
    TrieNode *p=root;
    int len=strlen(str);
    for(int i=0;i<len;++i){
        int id=str[i]-'a';
        if(p->next[id]==NULL)
            return 0;
        p=p->next[id];
    }
    return p->ti;
}

int Behind(TrieNode *root,char *str){
    TrieNode *p=root;
    int len=strlen(str);
    int ans=0;
    for(int i=0;i<len;++i){
        int id=str[i]-'a';
        for(int j=id+1;j<Max;++j)
            if(p->next[j]!=NULL)
                ans+=p->next[j]->ti;
        p=p->next[id];
    }
    for(int i=0;i<Max;++i)
        if(p->next[i]!=NULL)
            ans+=p->next[i]->ti;
    return ans;
}

int n,m,ans;
char str[50005];
char now[50006];

int main(){
    TrieNode *root=new TrieNode();
    scanf("%d%d",&n,&m);
    int sum=0,ans=0;
    char ch=getchar();
    scanf("%s",str);
    for(int i=0;i<n;++i){
        if(i+m>n-1){
            int j=0;
            for(int k=i;k<=n;++k,++j)
                now[j]=str[k];
        }
        else for(int j=0,k=i;j<m;++k,++j)
            now[j]=str[k];
        Insert(root,now);
        ans+=Behind(root,now);
    }
    printf("%d",ans);
    delete root;
    return 0;
}

用vector乱搞的(只能过6个测试点)

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<vector>
#include<cctype>
#include<cstdio>
#include<string>
#include<cstdio>
#define N 50005
using namespace std;
 
inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch))    
        ch=getchar();
    while(isdigit(ch))
        x=x*10+ch-'0',ch=getchar();
    return x*f;
}
 
vector<string>a[27];
void insert(string x,int i){
    a[i].insert(upper_bound(a[i].begin(),a[i].end(),x),x);
    return;
}
int find(string x,int i){
    return upper_bound(a[i].begin(),a[i].end(),x)-a[i].begin();
}

long long  ans;
int n,m;
string str;
int sum[N];

int main(){
    n=read(),m=read();
    cin>>str;
    string now;
    int len=str.size();
    for(int i=0;i<len;++i){
        if(i+m-1>len-1)
            now=str.substr(i,len-i);
        else now=str.substr(i,m);
        int shou=now[0]-'a';
        for(int j=shou;j<26;++j)ans+=sum[j];
        ans-=find(now,shou);
        insert(now,shou);
        sum[shou]++;
    }
    cout<<ans;
    return 0;
}

然后作死写了个可持久化Treap代替缓慢的vector
然后过了9个点(如果把内存开到512MB的话)
然后最后第八个数据点死活过不去(100s,950MB内存)
原因当然是因为string啦
但是有没有切实可行的能代替string的解决方案
我还真是不知道

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<vector>
#include<cctype>
#include<cstdio>
#include<string>
#include<cstdio>
#define N 50005
using namespace std;
 
inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch))    
        ch=getchar();
    while(isdigit(ch))
        x=x*10+ch-'0',ch=getchar();
    return x*f;
}
class Treap{
    private:
    int com,x,y,z,a,b;
    int root;
    int ch[N][3];
    string val[N];
    int pri[N];
    int siz[N];
    int sz;
    void update(int x){
        siz[x]=1+siz[ch[x][0]]+siz[ch[x][1]];
    } 
    int new_node(string v){
        siz[++sz]=1;
        val[sz]=v;
        pri[sz]=rand(); 
        return sz;
    }
    int merge(int x,int y){
        if(!x||!y)return x+y;
        if(pri[x]<pri[y]){
            ch[x][1]=merge(ch[x][1],y); 
            update(x);
            return x;
        } 
        else{
            ch[y][0]=merge(x,ch[y][0]);
            update(y);
            return y;
        }
    }
    void split(int now,string k,int &x,int &y){
        if(!now)x=y=0;
        else{
            if(val[now]<=k)    
                x=now,split(ch[now][1],k,ch[now][1],y);
            else 
                y=now,split(ch[now][0],k,x,ch[now][0]);
            update(now);
        } 
    }
    public:
    Treap(){
        root=0;
    }
    void Insert(string a){       
        split(root,a,x,y);
        root=merge(merge(x,new_node(a)),y);
    }
    int Find(string a){
        split(root,a,x,y);
        int ans=siz[x];
        root=merge(x,y);
        return ans;
    }
};


int ans;
int n,m;
string str;
int sum[N];
Treap BBT[27];


int find(int a,string str){
    return BBT[a].Find(str);
}

int main(){
    n=read(),m=read();
    cin>>str;
    string now;
    int len=str.size();
    for(int i=0;i<len;++i){
        if(i+m-1>len-1)
            now=str.substr(i,len-i);
        else now=str.substr(i,m);
        int shou=now[0]-'a';
        for(int j=shou;j<26;++j)ans+=sum[j];
        ans-=find(shou,now);
        BBT[shou].Insert(now);
        sum[shou]++;
    }
    printf("%d\n",ans);
    return 0;
}

但是所有被存储在平衡树中的字符串都是输进来的那个字符串的一个子串
然后对于这个题,我们就可以搞点事情
用一个结构体(pair)存储一个字符串在母串中的开端和结束
然后比较直接将字符串拖出来比较(反正暴力比较不会超时)
这样就省去了巨大的空间开销
但是这个东西并不能代替string
而且非常狗彘的是这个程序过不了第九和第十个数据
正好和string的相反string过不了第八个数据
原因很简单
第八个点是随机字符串
第九、十个点是相同的字符偏多
所以\(O(n)\)比较非常慢

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<vector>
#include<cctype>
#include<cstdio>
#include<string>
#include<cstdio>
#define N 50005
using namespace std;
 
inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch))    
        ch=getchar();
    while(isdigit(ch))
        x=x*10+ch-'0',ch=getchar();
    return x*f;
}


char str[N];

struct si{
    int l,r;
};

int ii=0,jj=0;

bool QxHd(si a,si b){
    ii=a.l,jj=b.l;
    int lena=a.r-a.l+1,lenb=b.r-b.l+1;
    for(register int k=0;k<min(lena,lenb);++k)
        if(str[ii+k]<str[jj+k])return true;
        else if(str[ii+k]==str[jj+k])continue;
        else return false;
    return lena<=lenb;
}

class Treap{
    private:
    int com,x,y,z,a,b;
    int root;
    int ch[N][3];
    si val[N];
    int pri[N];
    int siz[N];
    int sz;
    void update(int x){
        siz[x]=1+siz[ch[x][0]]+siz[ch[x][1]];
    } 
    int new_node(si asd){
        siz[++sz]=1;
        val[sz]=asd;
        pri[sz]=rand(); 
        return sz;
    }
    int merge(int x,int y){
        if(!x||!y)return x+y;
        if(pri[x]<pri[y]){
            ch[x][1]=merge(ch[x][1],y); 
            update(x);
            return x;
        } 
        else{
            ch[y][0]=merge(x,ch[y][0]);
            update(y);
            return y;
        }
    }
    void split(int now,si &k,int &x,int &y){
        if(!now)x=y=0;
        else{
            if(QxHd(val[now],k))    
                x=now,split(ch[now][1],k,ch[now][1],y);
            else 
                y=now,split(ch[now][0],k,x,ch[now][0]);
            update(now);
        } 
    }
    public:
    Treap(){
        root=0;
    }
    void Insert(si &a){       
        split(root,a,x,y);
        root=merge(merge(x,new_node(a)),y);
    }
    int Find(si &a){
        split(root,a,x,y);
        int ans=siz[x];
        root=merge(x,y);
        return ans;
    }
};


int ans;
int n,m;

int sum[N];
Treap BBT[27];


int find(int a,si asd){
    return BBT[a].Find(asd);
}

int main(){
    n=read(),m=read();
    scanf("%s",str);
    si now;
    int len=strlen(str);
    for(int i=0;i<len;++i){
        if(i+m-1>len-1)
            now=si{i,len-1};
        else now=si{i,i+m-1};
        int shou=str[now.l]-'a';
        for(int j=shou;j<26;++j)ans+=sum[j];
        ans-=find(shou,now);
        BBT[shou].Insert(now);
        sum[shou]++;
    }
    printf("%d",ans);
    return 0;
}

最后一个优化
将字符串比较器用hash二分比较优化成\(O(log(n))\)
然后就可以300MS左右通过这道题目了
但是我没有调出我的hash来……
正解没我快,hahaha
虽然我是全WA掉了

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<vector>
#include<cctype>
#include<cstdio>
#include<string>
#include<cstdio>
#define N 50005
using namespace std;

int n,m;

int sum[N];
int hash[N];
int pow[N];

inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch))    
        ch=getchar();
    while(isdigit(ch))
        x=x*10+ch-'0',ch=getchar();
    return x*f;
}

char str[N];
const int Mod=1e9+7;

bool QxHd(int la,int lb){
    if(la==lb)return true;
    int l,r,mid;
    long long hsi,hsj;
    l=0,r=m+1;
    if(la+m-1>n-1)r=n-1;
    if(lb+m-1>n-1)r=n-1;
    while(l<r){
        mid=(l+r)>>1;
        hsi=(hash[la+mid-1]-hash[la-1]+Mod)%Mod;
        hsj=(hash[lb+mid-1]-hash[lb-1]+Mod)%Mod;
        if(hsi!=hsj)r=mid-1;
        else l=mid+1;
    }
    if(l==m)return true;
    return str[la+l]<=str[lb+l];
}

class Treap{
    private:
    int com,x,y,z,a,b;
    int root;
    int ch[N][3];
    int val[N];
    int pri[N];
    int siz[N];
    int sz;
    void update(int x){
        siz[x]=1+siz[ch[x][0]]+siz[ch[x][1]];
    } 
    int new_node(int asd){
        siz[++sz]=1;
        val[sz]=asd;
        pri[sz]=rand(); 
        return sz;
    }
    int merge(int x,int y){
        if(!x||!y)return x+y;
        if(pri[x]<pri[y]){
            ch[x][1]=merge(ch[x][1],y); 
            update(x);
            return x;
        } 
        else{
            ch[y][0]=merge(x,ch[y][0]);
            update(y);
            return y;
        }
    }
    void split(int now,int k,int &x,int &y){
        if(!now)x=y=0;
        else{
            if(QxHd(val[now],k))    
                x=now,split(ch[now][1],k,ch[now][1],y);
            else 
                y=now,split(ch[now][0],k,x,ch[now][0]);
            update(now);
        } 
    }
    public:
    Treap(){
        root=0;
    }
    void Insert(int &a){       
        split(root,a,x,y);
        root=merge(merge(x,new_node(a)),y);
    }
    int Find(int &a){
        split(root,a,x,y);
        int ans=siz[x];
        root=merge(x,y);
        return ans;
    }
};

Treap BBT[27];
int ans;


int find(int a,int asd){
    return BBT[a].Find(asd);
}

void Hash(char *st){
    hash[0]=0;
    pow[0]=1;
    for(int i=0;i<n;++i){
        hash[i+1]=(hash[i]*29+st[i]-'a')%Mod;
        pow[i+1]=pow[i]*29%Mod;
    }
}

int main(){
    n=read(),m=read();
    scanf("%s",str);
    Hash(str);
    int now;
    int len=strlen(str);
    for(int i=0;i<len;++i){
        now=i;
        int shou=str[now]-'a';
        for(int j=shou;j<26;++j)ans+=sum[j];
        ans-=find(shou,now);
        BBT[shou].Insert(now);
        sum[shou]++;
    }
    printf("%d",ans);
    return 0;
}

转载于:https://www.cnblogs.com/qdscwyy/p/7786657.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: SpringBoot SQL某学校题库管理系统是一个基于SpringBoot框架和SQL数据库开发的系统,用于管理学校的题库资源。该系统将提供以下主要功能和特点。 1. 用户管理:系统支持多级用户权限管理,包括管理员、教师、学生等角色。管理员可以添加、修改和删除用户信息,教师可以上传和编辑题目,学生可以查看和答题。 2. 题目管理:教师可以通过系统上传、编辑和删除题目。系统将提供丰富的题目类型,包括单选题、多选题、填空题等。教师可以对题目进行分类和标签,方便学生根据需求进行搜索和筛选。 3. 题库查询:学生可以通过系统查询题目,并根据不同的标签和分类进行筛选。系统将提供题目的详细信息和答案,方便学生进行学习和复习。 4. 试题生成:系统可以根据教师的要求自动生成试题,包括随机选取题目、指定题目数量和难度等。教师可以根据需要生成试卷,方便学生进行考试。 5. 考试管理:系统可以记录学生的考试成绩和答题情况。教师可以查看学生的答题情况和分数,方便对学生进行评估和反馈。 6. 数据备份和恢复:系统将提供数据备份和恢复功能,确保数据的安全性和可靠性。 该系统将采用SpringBoot框架进行开发,使用SQL数据库进行数据存储和管理。通过使用SpringBoot,系统具有高效、便捷和可扩展的特点。同时,SQL数据库可以提供良好的数据管理和查询性能,满足系统对数据的存储和检索需求。 该题库管理系统将极大地提高学校对题库资源的管理效率和学生的学习体验。教师可以方便地上传和编辑题目,学生可以便捷地查询和答题。系统的数据备份和恢复功能也确保了数据的安全性。 ### 回答2: 某学校题库管理系统使用了Spring Boot和SQL来实现。 Spring Boot是一个快速开发框架,它简化了Java应用程序的搭建和配置过程。它提供了自动化配置和快速启动的特性,可以帮助我们快速构建可靠且可扩展的应用程序。在题库管理系统中,我们使用Spring Boot作为后端框架,提供数据存储和业务逻辑处理的支持。 SQL(Structured Query Language)是一种用于管理关系数据库的语言。在题库管理系统中,我们使用SQL来操作数据库,包括创建题目、存储学生答案和生成成绩报告等功能。通过SQL,我们可以对数据库中的数据进行增删改查的操作,实现对题库和学生信息的管理。 题库管理系统的主要功能包括题目管理、学生答题和成绩统计。题目管理功能允许管理员添加、编辑和删除题目,包括题目的题目类型、题目内容和正确答案等信息。学生答题功能允许学生选择题目并提交答案,系统会保存学生的答案并与正确答案进行比对。成绩统计功能会根据学生答题的情况生成成绩报告,包括每道题目的得分和总体成绩等信息。 通过Spring Boot和SQL的结合,我们可以快速搭建一个学校题库管理系统,并实现题目管理、学生答题和成绩统计等功能。这样的系统可以方便地管理学生的答题情况,并提供成绩统计的功能,帮助学校和教师更好地评估学生的学习情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值