【算法题解】 数据分类处理

261 阅读7分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第29天,点击查看活动详情

描述

信息社会,有海量的数据需要分析处理,比如公安局分析身份证号码、 QQ 用户、手机号码、银行帐号等信息及活动记录。

采集输入大数据和分类规则,通过大数据分类处理程序,将大数据分类输出。

数据范围:1I,R1001≤I,R≤100 ,输入的整数大小满足0≤val≤2^31^−1\

输入描述:

一组输入整数序列I和一组规则整数序列R,I和R序列的第一个整数为序列的个数(个数不包含第一个整数);整数范围为0~(2^31)-1,序列个数不限

输出描述:

从R依次中取出R,对I进行处理,找到满足条件的I:

I整数对应的数字需要连续包含R对应的数字。比如R为23,I为231,那么I包含了R,条件满足 。

按R从小到大的顺序:

(1)先输出R

(2)再输出满足条件的I的个数;

(3)然后输出满足条件的I在I序列中的位置索引(从0开始);

(4)最后再输出I。

附加条件:

(1)R需要从小到大排序。相同的R只需要输出索引小的以及满足条件的I,索引大的需要过滤掉

(2)如果没有满足条件的I,对应的R不用输出

(3)最后需要在输出序列的第一个整数位置记录后续整数序列的个数(不包含“个数”本身)

序列I:15,123,456,786,453,46,7,5,3,665,453456,745,456,786,453,123(第一个15表明后续有15个整数)

序列R:5,6,3,6,3,0(第一个5表明后续有5个整数)

输出:30, 3,6,0,123,3,453,7,3,9,453456,13,453,14,123,6,7,1,456,2,786,4,46,8,665,9,453456,11,456,12,786

说明:

30----后续有30个整数

3----从小到大排序,第一个R为0,但没有满足条件的I,不输出0,而下一个R是3

6--- 存在6个包含3的I

0--- 123所在的原序号为0

123--- 123包含3,满足条件 \

示例1

输入:
15 123 456 786 453 46 7 5 3 665 453456 745 456 786 453 123
5 6 3 6 3 0
输出:
30 3 6 0 123 3 453 7 3 9 453456 13 453 14 123 6 7 1 456 2 786 4 46 8 665 9 453456 11 456 12 786
说明:
将序列R:5,6,3,6,3,0(第一个5表明后续有5个整数)排序去重后,可得0,3,6。
序列I没有包含0的元素。
序列I中包含3的元素有:I[0]的值为123I[3]的值为453I[7]的值为3I[9]的值为453456I[13]的值为453I[14]的值为123。
序列I中包含6的元素有:I[1]的值为456I[2]的值为786I[4]的值为46I[8]的值为665I[9]的值为453456I[11]的值为456I[12]的值为786。
最后按题目要求的格式进行输出即可。   

题目的主要信息:

  • 一组输入整数序列I和一组规则整数序列R,I和R序列的第一个整数为序列的个数(个数不包含第一个整数)
  • 整数范围为0至0xFFFFFFFF,序列个数不限
  • RR依次中取出RiR_i,对II进行处理,找到满足条件的Ij:IjI_j:I_j整数对应的数字需要连续包含RiR_i对应的数字,按照RiR_i从小到大的顺序处理,重复元素只处理第一个
  • 对于每个RiR_i如果有满足上述条件的IjI_j,先输出RiR_i,没有就不输出,再输出满足条件的IjI_j的个数,然后输出满足条件的全部索引jj(从0开始),最后再输出全部的IjI_j
  • 最后需要在输出序列的第一个整数位置记录后续整数序列的个数(不包含“个数”本身)
  • 需要处理多组输入的情况

方法一:排序+暴力验证

具体做法:

我们按照题目要求按部就班处理就可以了:

首先根据第一个输入得到I序列的长度n,然后用vector存储I,然后根据输入得到R的长度m,同样利用vector数组存储R。

处理完输入以后先用sort函数对R序列排序。

然后遍历序列R,遇到与前一个相同的元素,我们跳过,以此来去重。然后遍历序列I,检查每一个IjI_j中是否包含了连续的RiR_i,如果有我们计数加1,并临时记录符合条件的下标。

如果上面的计数为0,我们不做任何处理。如果不为0,我们将RiR_i加入待输出数组末尾,再将刚刚的计数加在后面,最后遍历刚刚记录下标的数组,依次将下标jj及对应的元素IjI_j加入待输出数组后面。

最后我们统计待输出的数组长度,先在输出数组前输出就行了。

验证的时候,我们首先比较二者大小,然后找到每次i要除10的多少次方余数才能和r比较,然后我们就连续对i除这个值,每次比较余数即可。

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

bool check(int r, int i){//检查数字i包含数字r
    if(r > i) //i比r还小,不可能
        return false;
    int top = 1;
    while(r / top != 0) //记录每次要连续除10的多少次方,余数才能和r比较
        top *= 10;
    if(r == 0)
        top = 10;
    int k = 0;
    while(i * 10 / top != 0){ //连除比较
        if((i % top) == r)
            return true;
        i /= 10;
    }
    return false;
}
int main(){
    int n, m; //记录序列I和序列R的大小
    while(cin >> n){
        vector<int> I(n);
        for(int i = 0; i < n; i++)
            cin >> I[i]; //输入n个整数序列I
        cin >> m; //整数序列R的长度
        vector<int> R(m);
        for(int i = 0; i < m; i++) //输入m个整数序列R
            cin >> R[i];
        sort(R.begin(), R.end()); //排序
        vector<int> res;
        for(int i = 0; i < m; i++){
            if(i != 0 && R[i] == R[i - 1]) //去重
                continue;
            int count = 0;
            vector<int> index; //记录符合条件的下标
            for(int j = 0; j < n; j++){
                if(check(R[i], I[j])){
                    count++;
                    index.push_back(j);
                }
            }
            if(count != 0){ //如果有出现连续的R[i],添加头部的R[i]及后面有多少个数中出现了
                res.push_back(R[i]);
                res.push_back(count);
                for(int j = 0; j < index.size(); j++){
                    res.push_back(index[j]);
                    res.push_back(I[index[j]]);
                }
            }
        }
        cout << res.size(); //先输出个数
        for(int i = 0; i < res.size(); i++) //再逐个输出
            cout << " " << res[i];
        cout << endl;
    }
    return 0;
}

复杂度分析:

时间复杂度:O(nm+mlog2m)O(nm+mlog2m),其中n为序列I的长度,m为序列R的长度,我们要对于每个RiR_i会对每个IjI_j进行验证,验证的复杂度是常数,因为是数字的十进制位数,而我们也不知道m和n的具体大小,因此排序复杂度O(mlog2m)O(mlog2m),要加在后面 空间复杂度:O(n)O(n),辅助数组的最大长度为I序列长度,res数组及保存序列的数组都属于必要空间,不属于额外空间

方法二:哈希表+字符串

具体做法:

对于方法一的排序+去重,我们可以用有序哈希表map实现,输入的R序列直接进入哈希表,会自动排序去重,后续遍历哈希表就可以了。

而数组作为输出很麻烦,验证是否满足条件也很麻烦,我可以将数字转换成字符串,利用字符串的find函数查找是否出现过,然后所有的待输入的信息我们可以添加到字符串末尾,也可以在其前面添加数量信息及数字RiR_i

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

int main(){
    int n, m; //记录序列I和序列R的大小
    while(cin >> n){
        vector<int> I(n);
        for(int i = 0; i < n; i++)
            cin >> I[i]; //输入n个整数序列I
        cin >> m; //整数序列R的长度
        map<int, int> R;
        for(int i = 0; i < m; i++){ //哈希表记录序列R,直接去重加排序
            int t;
            cin >> t;
            R[t] = 1;
        }
        int total = 0;
        string res = ""; //以字符串的形式输出
        for(auto iter = R.begin(); iter != R.end(); iter++){
            int count = 0;
            string temp = ""; //记录这一轮的R[i]有多少匹配的
            for(int i = 0; i < n; i++){
                if(to_string(I[i]).find(to_string(iter->first)) != string::npos){ //找到出现数字
                    count++; //数量加1
                    temp += to_string(i) + ' ' + to_string(I[i]) + ' '; //添加索引和该数字
                }
            }
            if(count != 0){ //如果有出现连续的R[i],添加头部的R[i]及后面有多少个数中出现了
                res += to_string(iter->first) + ' ' + to_string(count) + ' ' + temp;
                total += (2 * count + 2); //补充后面增加了多少数
            }
        }
        res = to_string(total) + ' ' + res; //添加总个数
        cout << res << endl;
    }
    return 0;
}

复杂度分析:

  • 时间复杂度:O(nm+mlog2m)O(nm+mlog2m),其中nn为序列II的长度,mm为序列RR的长度,我们要对于每个RiR_i会对每个IjI_j进行验证,验证的复杂度是常数,因为是数字的十进制位数,而我们也不知道mmnn的具体大小,因此排序复杂度O(mlog2m)O(mlog2m),要加在后面
  • 空间复杂度:O(m)O(m),辅助空间为哈希表的长度,res字符串及保存序列的数组都属于必要空间,不属于额外空间