字符串存储正整数和查找比较 —— [NOIP2017]图书管理员

题目如下:

题目 图书管理员

图书馆中每本书都有一个图书编码,可以用于快速检索图书,这个图书编码是一个正整数。

每位借书的读者手中有一个需求码,这个需求码也是一个正整数。如果一本书的图书编码恰好以读者的需求码结尾,那么这本书就是这位读者所需要的。

小 D 刚刚当上图书馆的管理员,她知道图书馆里所有书的图书编码,她请你帮她写一个程序,对于每一位读者,求出他所需要的书中图书编码最小的那本书,如果没有他需要的书,请输出-1。

输入

输入的第一行,包含两个正整数 n 和 q,以一个空格分开,分别代表图书馆里书的数量和读者的数量。 接下来的 n 行,每行包含一个正整数,代表图书馆里某本书的图书编码。 接下来的 q 行,每行包含两个正整数,以一个空格分开,第一个正整数代表图书馆里读者的需求码的长度,第二个正整数代表读者的需求码。

输出

输出有 q 行,每行包含一个整数,如果存在第 i 个读者所需要的书,则在第 i 行输出第 i 个读者所需要的书中图书编码最小的那本书的图书编码,否则输出-1。

备注
对于 20%的数据,1 ≤ n ≤ 2。 另有 20%的数据,q = 1。
另有 20%的数据,所有读者的需求码的长度均为1。
另有 20%的数据,所有的图书编码按从小到大的顺序给出。
对于 100%的数据,1 ≤ n ≤1,000,1 ≤ q ≤ 1,000,所有的图书编码和需求码均不超过 10,000,000。

题目很长,但是将题干翻译过来,其实就是输入几个数字,然后让你去查找符合后缀的数字而已。对此,我们只需要先把输入的图书编号添加某种标签后进行排序,再查找是否存在符合条件的最小编号即可。

那么要解决的问题可以拆分成几个小问题:

1、如何去存储数字?

2、如何去比较数字的大小?

3、如何根据需求码(后缀)快速找到符合要求的数字?

由于要考虑后缀的问题,我选取利用字符串的方式去存储数字,这里为了提高对结构体的熟练程度,我定义了如下结构体:

struct Book{
    string index;
    string info;
    Book(string iptIndex) : index(iptIndex) {}
};

不必要地去考虑了实用性,结构体里面除了书籍编号,还有书籍的信息(尽管并没有用上)。

接下来就是去比较数字的大小,字符串全是数字,从通用性考虑,应当先比较字符串的长度(正如同百位数一定大于十位数一样),再依次去比较最高、次高、次次高位......:如果比较的位数数字不一样,那么就能判断两个字符串代表的数字的大小,依据此思路,则有:

bool compareNumbers(Book booka, Book bookb) {
    string a= booka.index;
    string b= bookb.index;
    int asize = a.size();
    int bsize = b.size();
    if (asize != bsize) {
        return asize < bsize;
    }
    for (int i = 0; i < asize; i++) {
        if (a[i] != b[i]) {
            return a[i] < b[i];
        }
    }
    return false;
}

之后就是去搞索引,思路是按照字符串的长度和字符串的末尾

    vector<vector<Book>> Database(100);

对于Database[ij],其中ij是一个十位数,i为十位上的数字,j为个位上的数字,里面的Book类型元素book都满足:book.index的长度为i,且末尾为j(由于按长度分类,故比较函数中先比较两个index的长度便不必要)。然后根据读者的需求的最后一位t从低长度到高长度,检查Database[it]的元素是否符合要求。

完整代码实现如下:

#include<bits/stdc++.h>
#include<algorithm>
#include<cstdlib>
#include<cstring>
using namespace std;

struct Book{
    string index;
    string info;
    Book(string iptIndex) : index(iptIndex) {}
};

bool compareNumbers(Book booka, Book bookb) {
    string a= booka.index;
    string b= bookb.index;
    int asize = a.size();
    int bsize = b.size();
    if (asize != bsize) {
        return asize < bsize;
    }
    for (int i = 0; i < asize; i++) {
        if (a[i] != b[i]) {
            return a[i] < b[i];
        }
    }
    return false;
}

int main(){
    vector<vector<Book>> Database(100);
    int n,q;
    string bookindex;
    cin >> n >> q ;
    for(int i=0;i<n;i++){
        cin >> bookindex;
        int digit = bookindex.size();
        int tail = stoi(bookindex.substr(bookindex.size()-1,1));
        Database[digit*10+tail].push_back(Book(bookindex));
    }
    for(int i=10;i<100;i++){
        sort(Database[i].begin(),Database[i].end(),compareNumbers);
    }
    vector<string> need(q);
    int unused;
    for(int i=0;i<q;i++){
        cin >> unused >> need[i];
    }
    for(int i=0;i<q;i++){
        int digit = need[i].size();
        int tail = stoi(need[i].substr(need[i].size()-1,1));
        bool found = false;
        for(int j=1;j<=9;j++){
            for(Book book : Database[j*10+tail]){
                if(book.index.size()>=digit){
                    string xtail = book.index.substr(book.index.size()-digit,digit);
                    if(xtail == need[i]){
                        cout << book.index << endl;
                        found = true;
                        break;
                    }
                }
            }
            if(found) break;
        }
        if(!found) cout << "-1\n" ;
    }
    return 0;
}

值得注意的是,原先的代码中这一部分

 if(book.index.size()>=digit){
     string xtail = book.index.substr(book.index.size()-digit,digit);
     if(xtail == need[i]){
          cout << book.index << endl;
          found = true;
          break;
     }
 }

第一次写的时候并没有第一行的判断,这导致了测试用例的数据越界报错。我反复检查了数组存储时是否有越界,显然读取存储这一部分是没有问题的。最终才意识到运用substr函数时应当确认传入的参数合法,也就是提取的子串位置应当在母串内,这是我先前从未遇见的,这提示我日后编写代码时,需要关注传入函数的参数是否合法。

感谢你能看到这里。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值