CCF-CSP认证考试准备第十二天 201903-3 损坏的RAID5(大模拟)


今天开始第3题大模拟,加油!
### Day12: 1.201903-3

#### 1.201903-3:损坏的RAID5(大模拟)
(1)本题的难点为理解题意(**比较难**)然后找规律和异或运算
题目可以这样理解:
1.先介绍**RAID5基本算法**,基本原理是异或运算,即在 p 与 a1…an 这 (n+1)个数中,由任意 n个可以推知其余的一个
2.介绍硬盘的基本单位为**块**
3.介绍条带:**条带**是指连续、等大的数据块,是 RAID5 进行数据布局的基本单元。条带就是多个块的抽象集合,一个存储单元。**规律1:如果将条带也从 0开始编号,且条带大小为 s个块,则编号为 b 的块所在的条带编号是[b / s]**
4.介绍效验盘和存储盘的选择规则(**按行来**)(所以代码先计算行数)
5.目标题目该型号硬盘的块大小是 4字节(**基础知识:16进制位2个字符为1个字节(因为1个字符为1个16进制位,为4个二进制位,而一个字节为8个二进制位),所以最后输出8个字符**)
6.介绍输入格式(其中长度相等且为 8的整数倍的字符串,就可能要计算是8的几倍)和输出格式(如果该读操作由于下列情形之一无法进行,则输出一个减号(`-`):
- 阵列不完整,且被读取的块所在的硬盘缺失,且该数据无法由现存的硬盘数据推算出来;
- 指定的块超出阵列总长度。
特殊情况要提前和单独讨论)
7.**理解样例**:
样例1好理解;
样例2的图着重理解:
![[201903-3.png]]
图中,用虚线隔开的长方形表示一个块,连续的两个长方形组成的正方形表示一个条带。
(2)代码(100):
```
#include

using namespace std;

int char2digit(char c){
    if(isdigit(c))    return c-'0';
    else    return c-'A'+10;
}

char digit2char(int x){
    if(x>=0 && x<10)    return x+'0';
    else    return x-10+'A';
}

void yihuo(string &s1,string s2){
    for(int i=0;i<8;i++){
        s1[i]=digit2char(char2digit(s1[i]) ^ char2digit(s2[i]));
    }
}

int main(){
    ios::sync_with_stdio(false);
    int n,s,l;
    cin>>n>>s>>l;
    vector vs(n);//初始化为空字符串 
    int len=0;
    for(int i=0;i         int x;
        string str;
        cin>>x>>str;
        vs[x]=str;
        len=str.size();
    }
    int m;
    cin>>m;
    while(m--){
        int b;
        cin>>b;
        if(b>=(n-1)*len/8){
            cout<<"-"<             continue;
        }
        int row=b/(s*(n-1));//b号块所在行数
        int xiaoyanDiskIndex=n-1-row%n;//计算b号块这一行的效验盘索引
        int diskIndex=(xiaoyanDiskIndex+(b-s*(n-1)*row)/s+1)%n;//b号块所在的盘索引
        int blockIndex=row*s+b%s;//b号块在当前盘的索引
        string res;
        if(!vs[diskIndex].empty()){
            res=vs[diskIndex].substr(blockIndex*8,8);//substr用法
        }
        else{
            if(n-l>=2){
                cout<<"-"<                 continue;
            }
            string tmp;
            for(int i=0;i                 if(i==diskIndex)    continue;
                tmp=vs[i].substr(blockIndex*8,8);
                if(res.empty()){
                    res=tmp;
                }
                else{
                    yihuo(res,tmp);
                }
            }
            
        }
        cout<     }
    return 0;
}
```
(3)代码重点部分:
1.以函数的形式实现字符和整数的转化(因为异或运算`^`只能是数字),以及单个字符逐个异或(可以改善的部分,不过我这个比较好理解,简单)
2.通过找规律来理解本题的核心部分
```
int row=b/(s*(n-1));//b号块所在行数
int xiaoyanDiskIndex=n-1-row%n;//计算b号块这一行的效验盘索引
int diskIndex=(xiaoyanDiskIndex+(b-s*(n-1)*row)/s+1)%n;//b号块所在的盘索引
int blockIndex=row*s+b%s;//b号块在当前盘的索引
```
**抽象拓展**:
##### Step 1: **理解问题的结构**
需要明确每个块(或数据单元)是如何分配到不同的磁盘(或存储单元)的(**即数据的分布规律**),然后写出能够根据块编号定位磁盘和偏移量的公式(本题的行数是关键,不同行的效验盘索引不一样(题目告知),通过效验盘索引可以推出b号块所在的盘索引(因为题目是跳过效验盘来分布块),再获取b号块在当前盘的索引)。
##### Step 2: **找到动态变化的规则**
- 本题的关键在于校验盘的动态轮换,因此需要找出每一行数据中哪个磁盘存储校验块,其余存储数据块。
- 如果有类似的轮换机制,可以通过观察找到一个模式,像 `xiaoyanDiskIndex` 的计算公式就是通过观察校验盘随行号变化的规律得出的。
##### Step 3: **将动态变化与静态数据结合**
- 在找到校验盘后,你需要考虑剩余的盘如何存储数据。这里需要跳过校验盘,并根据条带大小、数据分块等静态数据计算出具体的存储位置。
- 在公式中,`diskIndex` 的计算需要结合校验盘的索引来跳过校验盘,同时保持对剩余盘的数据存储进行定位。
##### Step 4: **逐步调试公式**

- 在写完公式后,通过实例化几个具体的块编号,验证计算出来的盘号、行号和块偏移量是否正确。通过调试,逐步完善规律公式。
3.**注意**:
3.1:字符串的初始化和空字符串的判断:
不需要手动赋值" "和判断是不是为" ",下面更好用
```
vector vs(n);//初始化为空字符串
if(!vs[diskIndex].empty())
```
3.2:string的substr函数写法:
```
res=vs[diskIndex].substr(blockIndex*8,8);//没有迭代器,为索引加上长度,且前面加上是哪个字符串的substr
```

你可能感兴趣的:(CCF-CSP认证,算法,c++,开发语言)