项目介绍以及第一次面经

项目介绍

QA

  1. 介绍项目
    教育场景下多种行为识别的项目,是我研究生阶段第一个项目,也是第一次去解决一些结合实际业务场景,结合软硬件编程的一个项目。经过这个项目,基本上就强化了自己对项目问题的分析以及代码能力,规范化代码习惯,比如从一开始把所有代码不加规范的任意放置,到结合具体设计模式去进行封装,把功能去写成一个个类,用面向对象的方法去解决问题,以及打包编译成库进行发布与交付。当自己的代码在嵌入式芯片上成功运行时,有比较大的成就感。然后在商品识别的项目当中,事先是进行了大量的算法学习和查阅文档的工作的,而且在过程中,结合需求进行了功能的完善,从一开始只能去识别一个固定角度摄像头下的图片,到后面不断提升了精度和速度。

  2. 在项目中,学到了什么
    代码性能的优化,一方面是精度吧,后面的ransac有,另一方面是速度,也是结合硬件加速的引擎,Tensorrt,然后华为海思的nnie,然后多媒体处理模块。sift用了gpu加速,cuda编程,也是基于前人的肩膀,用cmake去实现的cpp工程,就可以直接将cuda文件类似于cpp一样的去封装到自己的项目当中。

  3. 遇到了什么问题,如何解决的
    有两个比较印象深刻的问题吧,一个是在算法层面,一个是在项目开发层面。在商品识别与匹配这个项目中,我们用了sift作为匹配算法,最一开始并没有考虑对其进行改进,后面遇到了匹配点过多,然后在卡阈值的时候有一些误判的时候,发现有很多点,由于光照、拍摄模糊程度等,其实是错误匹配的,所以需要将它们进行滤除。后面结合了最小二乘的思想,进行了随机抽样一致性的一个算法过滤。才有了更好的效果。另外,在训练神经网络方面,也是有一些技巧的,比如如何去数据增强,然后如何多GPU并行训练,以及网络结构的选取,如何解决多尺度目标检测,设置一些初始化参数等等。在项目开发方面,主要是一些由于前期考虑不周而给后面的继续开发造成的隐患。比如在智慧货架商品识别项目当中,一开始只是要求摄像头角度正拍,后面又要求有斜拍,用手机排,扩展功能。所以就得去事先进行代码模式的设计,而不是到用的时候再去重新封装改写。另外还有一个问题印象深刻,就是在使用第三方库的时候,没有深入理解其用法,就会容易想当然的去用,直接去套一些模板,但实际上并不符合,比如在opencv当中有一个imencode方法,其功能是将图片编码,但一定要指定是jpg还是png格式,因为图像格式不一样,其通道数不一样,那编码的方法也就不一样。我在本地调试的时候能够通过,但当打包交付时候,就会出现bug。

  4. 怎么样快速地融入团队?一方面是知识储备,另一方面是了解业务架构,边学习边做事

  5. 对自己学习能力如何判断?获得的成绩,项目经历,自学能力,从无到有。然后对自己的数学等理论功底还是有一些深入储备的。

  6. 平时学习怎么做到成绩名列前茅的?意识到学习的重要性,结合了自身的兴趣,然后学习的习惯,预习复习。保持这种状态,一直到毕业,就习惯性的把一些工作学习放到前面,不松懈有一种紧张感。

  7. 问,这个岗位具体是做什么类型的通讯呢?有哪些算法相关呢?

Star原则

Situation/Task 明确任务
1、任务类型和背景

Action 明确行动
1、现状分析
2、决定某种行动方式

Result 说明结果
结果怎样
从项目中学到了什么,经过一段时间的反思,认为项目中还有什么值得提升改进的地方
在测试时,遇到了什么问题,是如何解决的?

  1. 在图像算法相关项目中,遇到过输入为空的情况,这时候一般来说是用户的输入我们没有考虑周全,导致后面对空指针进行操作致使程序崩溃,需要加上用户的提示,防止程序崩溃。然后在算法中间处理的过程中,一般不会出什么问题,无非就是一些内存忘记释放导致泄漏的一些小问题,后经过修复都解决。最后输出阶段,由于需要将内存的数据编解码,解码时,是用jpg格式还是png格式,是否带图像透明度等一些小问题。
  2. 在前后端平台搭建的项目中,最核心遇到的问题其实还是网络通信的问题,比如在用html,跟一个网络摄像机进行搭建的时候,一开始用rtsp流,后来发现必须改成rtmp,然后还需要大家nginx服务器等一些操作。

视频理解项目:

  1. 项目目标:
  2. 任务难点:
  3. 任务分工:
  4. 解决方案:
  5. 评价指标:

目标分割项目:叶菜湿布

  1. 如何检测:erfnet
    模型沿用了Encoder-Decoder结构,结合残差网络、整个网络包含23层,其中1-16层为Encoder,17-23层为Decoder。

Encoder:
- DownsampleBlock
- non_bottleneck_1d

        self.initial_block = DownsamplerBlock(3, 16)

        self.layers = nn.ModuleList()

        for x in range(0, 5):  # 5 times
            self.layers.append(non_bottleneck_1d(16, 0.03, 1))

        self.layers.append(DownsamplerBlock(16, 64))

        self.layers.append(non_bottleneck_1d(64, 0.3, 2))
        self.layers.append(non_bottleneck_1d(64, 0.3, 4))
        self.layers.append(non_bottleneck_1d(64, 0.3, 8))
        self.layers.append(non_bottleneck_1d(64, 0.3, 16))

其中:DownsamplerBlock

self.conv = nn.Conv2d(ninput, noutput - ninput, (3, 3), stride=2, padding=1, bias=True)
self.pool = nn.MaxPool2d(2, stride=2)
self.bn = nn.BatchNorm2d(noutput, eps=1e-3)

其中,non_bottleneck_1d

super(non_bottleneck_1d, self).__init__()

        self.conv3x1_1 = nn.Conv2d(chann, chann, (3, 1), stride=1, padding=(1, 0), bias=True)

        self.conv1x3_1 = nn.Conv2d(chann, chann, (1, 3), stride=1, padding=(0, 1), bias=True)

        self.bn1 = nn.BatchNorm2d(chann, eps=1e-03)

        self.conv3x1_2 = nn.Conv2d(chann, chann, (3, 1), stride=1, padding=(1 * dilated, 0), bias=True,
                                   dilation=(dilated, 1))

        self.conv1x3_2 = nn.Conv2d(chann, chann, (1, 3), stride=1, padding=(0, 1 * dilated), bias=True,
                                   dilation=(1, dilated))

        self.bn2 = nn.BatchNorm2d(chann, eps=1e-03)

        self.dropout = nn.Dropout2d(dropprob)

其中,dilated conv膨胀卷积,也叫空洞卷积。结构图如下:
在这里插入图片描述

Decoder:
- UpsamplerBlock
- non_bottleneck_1d

其中UpsamplerBlock

self.conv = nn.ConvTranspose2d(ninput, noutput, 3, stride=2, padding=1, output_padding=1, bias=True)
self.bn = nn.BatchNorm2d(noutput, eps=1e-3)

在这里插入图片描述

loss function

使用的是cross_entropy2d(outputs, labels, weight = class_weight)
本质上,就是将两个输入,outputs即最后的feature map和label,都是二维,进行upsample操作到相同大小之后,进行view展平,然后经过cross_entropy操作即可。

def cross_entropy2d(input, target, weight=None, size_average=True):
    n, c, h, w = input.size()
    nt, ht, wt = target.size()

    # Handle inconsistent size between input and target
    if h > ht and w > wt:  # upsample labels
        target = target.unsequeeze(1)
        target = F.upsample(target, size=(h, w), mode="nearest")
        target = target.sequeeze(1)
    elif h < ht and w < wt:  # upsample images
        input = F.upsample(input, size=(ht, wt), mode="bilinear")
    elif h != ht and w != wt:
        raise Exception("Only support upsampling")

    input = input.transpose(1, 2).transpose(2, 3).contiguous().view(-1, c)
    target = target.view(-1)
    loss = F.cross_entropy(input, target, weight=weight, size_average=size_average, ignore_index=250)
    return loss
  1. 如何转化成项目需求:为什么检测蔬菜而不是检测布
  2. 如何分析波形,包括滤波,取分析面积等(具体细节还可能包括求连通域等)
  3. 如何沟通,本来是检测动作——>检测状态——>本来是检测既定时间内——>扩展10分钟——>本来是分析持续进行——>断点保存,可以同时运行多路

商品识别项目:

SIFT原理以及应用

SIFT全称:scale-invariant feature transform 。尺度不变特征变换,其实叫做尺度变换而特征不变更合理。
SIFT步骤
https://zhuanlan.zhihu.com/p/22476595

SIFT 算法具的特点

图像的局部特征,对旋转、尺度缩放、亮度变化保持不变,对视角变化、仿射变换、噪声也保持一定程度的稳定性
独特性好,信息量丰富,适用于海量特征库进行快速、准确的匹配。
多量性,即使是很少几个物体也可以产生大量的 SIFT 特征
高速性,经优化的 SIFT 匹配算法甚至可以达到实时性
扩招性,可以很方便的与其他的特征向量进行联合。
SIFT 特征检测的步骤有 4 个主要步骤

尺度空间的极值检测 :搜索所有尺度空间上的图像,通过高斯微分函数来识别潜在的对尺度和选择不变的兴趣点。
特征点定位 :在每个候选的位置上,通过一个拟合精细模型来确定位置尺度,关键点的选取依据他们的稳定程度。
特征方向赋值 :基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向, 后续的所有操作都是对于关键点的方向、尺度和位置进行变换,从而提供这些特征的不变性。
特征点描述 :在每个特征点周围的邻域内,在选定的尺度上测量图像的局部梯度, 这些梯度被变换成一种表示,这种表示允许比较大的局部形状的变形和光照变换。

步骤可以主要分成两步:

  1. 特征点检出
  2. 特征点描述
    特征点检出主要是用了DoG,就是把图像做不同程度的高斯模糊blur,平滑区域或点肯定变化不大,而纹理复杂的比如边缘,点、角之类区域肯定变化很大,这样变化很大的点就是特征点。当然,为了找到足够的点,还需要把图像放大缩小几倍(Image Pyramids)来重复这个步骤找特征点。其实DoG并不是Lowe提出的,可替代特征点检出的还有很多其他方法如MSER。

特征点描述就是一个简单的HOG,即以检出的特征点为中心选16x16的区域作为local patch,这个区域又可以均分为4x4个子区域,每个子区域中各个像素的梯度都可以分到8个bin里面,这样就得到了4x4x8=128维的特征向量。特征点检出以后还需要一个很重要的步骤就是归一化,计算这个patch的主方向,然后根据这个主方向把patch旋转到特定方向,这样计算的特征就有了方向不变性,也需要根据patch各像素梯度大小把patch缩放到一定的尺度,这样的特征就有了尺度不变性

归纳来说,SIFT算法的实质可以归为在不同尺度空间上查找特征点的问题。
在这里插入图片描述
https://blog.csdn.net/qq_39451645/article/details/112129039

其实确切来说SIFT是指第二步的特征,而SIFT features或SIFT descriptor的说法比较准确。
想要看数学解释:https://blog.csdn.net/jancis/article/details/80824793
想要通俗理解:https://blog.csdn.net/qq_39451645/article/details/112129039

GPU加速版SIFT

先detection yolov5 VOCDevkit商品数据集(公开和自行标注)

  1. 项目目标:找出颜色数目排名前三的颜色作为该图片的主颜色,通过两幅图片主颜色比对判定两幅图是否不一致。判定方式为:
    三种主要颜色在保证第一大类颜色必然能对应的同时,仍存在第二种颜色一致,否则认为两张图片不同。颜色一致可由SIFT或文字识别进一步比对。

为避免噪点影响,使用连通区域检测思想进行颜色不同类别数量统计(并查集)。
2. 基本知识:
检测颜色信息时往往用HSV空间而不是RGB空间,是因为RGB通道并不能很好的反映出物体具体的颜色信息。但HSV在用于指定颜色分割时,有比较大的作用,H和S分量代表了色彩信息。颜色距离代表两种颜色之间的数值差异。对于不同的色彩区域,混合H与S分量,划定阈值,即可进行简单的分割。

  • Hue用角度度量,取值范围为0~360°,表示色彩信息,即所处的光谱颜色的位置,Hue = 0表示红色,Hue = 120表示绿色,Hue = 240表示蓝色等。
    -在这里插入图片描述
    在这里插入图片描述

  • Saturation表示饱和度,饱和度表示颜色解决光谱色的程度。饱和度越高,说明颜色越深,越接近光谱色饱和度越低,说明颜色越浅,越接近白色。饱和度为0表示纯白色。取值范围为0~100%

  • 竖直方向表示明度,决定颜色空间中颜色的明暗程度,明度越高,表示颜色越明亮,范围是0~100%,明度为0表示纯黑色。

  1. 任务难点:
  • 颜色种类较少,图片描述过于模糊,不利于近似颜色比较。
  • 其他颜色物品反射光对颜色比较影响较大。
  • 分割单个商品时较低商品包含较多背景区域,引起误判。
  • 光线较暗时,颜色检测趋于灰黑。
  1. 主要负责:
    256维颜色分类判定距离
    合成特征矢量:L = 16 * H + 4 * S + V

距离判断

得到的结果距离越接近1,两幅图越相似,暂取0.5为对比阈值
得到两幅图特征矢量分别为X,Y,其距离为 (n=256)
D = ∑ 1 n m i n ( x i , y i ) m i n ( ∑ 1 n x i , ∑ 1 n y i ) \sum_1^n min(x_i,y_i) \over min(\sum_1^n x_i, \sum_1^n y_i) min(1nxi,1nyi)1nmin(xi,yi)

最大编辑距离(Levenshtein距离):是指两个字串之间,由一个转成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。一般来说,编辑距离越小,两个串的相似度越大。
https://leetcode-cn.com/problems/edit-distance/solution/shi-pin-jiang-jie-bian-ji-ju-chi-dong-tai-gui-hua-/

如何将该算法应用到分析商品陈列情况的呢?(学以致用环节,将商品唯一的条形码作为字符即可)
在这里插入图片描述
编辑过程:
str2=a b c d e f b c d
str1=b c d a b c d e f
1、向左走,即 d[ 1,1] = d[ 1-1,1 ]+1 , str2 要删除第一个字符,变为 bcdefbcd
2、斜向下,且值未变,说明相同,不用操作
str2 =b c d e f b c f
str1 =b c d a b c d e f
3、 d 之后向左,即删除 e
4、斜向下,且值加1,表示替换,将f换为a
str2 =b c d a b c f
str1 =b c d a b c d e f
5、最后两步向下,表示添加,此处添加 e, f
str2 = b c d a b c d e f
str1 = b c d a b c d e f

共5步操作。

回溯路径时要从右下角的元素开始,依次看当前元素是如何得到的,有时一个元素可能有多种得到的方式,即表明可以有多种操作可以得到相同的结果。上图的红色箭头即为回溯路径。将回溯路径再反过来就可以得到实际编辑操作的路径。

向左走,即dp[m][n] = dp[m - 1][n] + 1,表示删除一个字符
斜向下,且值未变,说明相同,不用操作
斜向下,且

https://blog.csdn.net/alansede/article/details/48103169

算法优化
为了使结果更加准确,将第一步判定认为一致的两幅图分别分为四份,将四份图片对应重复HSV判断,如果有两块及以上区域相似,认为两幅图相似。

  1. c++部署
    本地调试程序 ——> ARM平台打包静态库(因为没有安装交叉编译环境)——> 代码加密md5、模型加密 ——> 提交测试

  2. 评价指标:商品检测性能(瓶类)、陈列情况分析(是否正确)

生鲜项目

生鲜牌识别主要分为文字识别与数字识别两部分,文字识别主要采用paddleocr实现,数字识别则通过opencv对图片二值化处理后使用yolov3进行识别。部署于TX2上的yolov3采用tensorrt辅助加速

paddleOCR 3.5M中英文超轻量模型
包括自然场景文字识别和文档场景文字识别
在这里插入图片描述
文本检测、检测框矫正、文本识别

在这里插入图片描述
基于分割的文字识别方案!而不是检测(AAAI2020)
在这里插入图片描述

文本检测框矫正:几何变换和文本方向分类器
文本识别:CRNN,卷积特征和序列特征融合,挖掘特征上下文信息。CTC损失解决预测结果和标签不一致。神经网络预测的结果都是固定长度,但序列标签可能变化长
度。
ATTENTION机制或者transformer,在英文上效果不错,但是由于中文字符太多6000+,且由于中文语义的问题,挖掘两个字之间的上下文信息还是很困难的。(实验中,去掉lstm也不会很大幅度上影响检测准确率)

19个策略全方位优化瘦身:网络结构微调、数据增强、学习率变换策略、剪枝量化

合成数据(比如0~9数字)

backbone:mobilenet-V3(在之前深度可分离思想的基础上,使用h-wish而不是ReLU6,扩展层使用的滤波器数量不同(使用NetAdapt算法获得最佳数量),瓶颈层输出的通道数量不同(使用NetAdapt算法获得最佳数量),Squeeze-and-excitation模块(SE)将通道数仅缩减了3或4倍,对于SE模块,不再使用sigmoid,而是采用ReLU6(x + 3) / 6作为近似(就像h-swish那样))
https://zhuanlan.zhihu.com/p/260790699
https://zhuanlan.zhihu.com/p/70703846

智慧课堂校园大脑项目:

硬件调试技能和c++的熟练运用(静态库打包等)

计算机网络八股文

数据结构基础知识

  1. 最大堆最小堆的概念,
    堆树的定义如下:
    (1)堆树是一棵完全二叉树
    (2)堆树中某个节点的值总是不大于或不小于其子节点的值
    (3)堆树中每个节点的子树都是堆树
    当父节点的键值总是大于或等于任何一个子节点的键值时为最大堆。 当父节点的键值总是小于或等于任何一个子节点的键值时为最小堆
    堆树的操作
    (1)构造最大堆:
    首先将每个叶子节点视为一个堆,再将每个叶子节点与其父节点一起构成一个包含更多节点的堆。所以,在构造堆的时候,首先需要找到最后一个节点的父节点,从这个节点开始构造最大堆;直到该节点前面所有分支节点都处理完毕,这样最大堆就构造完毕。

    (2)最大堆中插入节点
    先在堆的最后添加一个节点,然后沿着堆树上升。跟最大堆的初始化过程大致相同。
    (3)最大堆中堆顶节点的删除
    将堆树的最后的节点提到根节点,然后删除最大值,然后再把新的根节点放到合适的位置。

    堆树的应用: 利用最大堆最小堆排序(堆排序算法详解:http://blog.csdn.net/guoweimelon/article/details/50904231)
    基本思想:从最大堆的顶部不断取走堆顶元素放到有序序列中,直到堆的元素被全部取完。算法流程如下:

    1. 创建一个最大(小)堆H
    2. 把堆首和堆尾元素互换
    3. 把堆的大小减一,重新构造一个最大(小)堆
    4. 重复步骤2、3,直到堆的大小为1
      https://blog.csdn.net/guoweimelon/article/details/50904346#:~:text=%E6%9C%80%E5%A4%A7%E5%A0%86%EF%BC%9A%E6%A0%B9%E7%BB%93%E7%82%B9,%E7%84%B6%E5%90%8E%E6%B2%BF%E7%9D%80%E6%A0%91%E4%B8%8A%E5%8D%87%E3%80%82
  2. 归并排序时间复杂度
    归并排序的性能不受输入数据的影响,但表现比选择排序好很多,因为始终都是O(nlogn)的时间复杂度。代价是需要额外的内存空间。
    归并排序(Merge Sort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法的一个非常典型的应用。作为一种典型的分而治之思想的算法应用,归并排序的实现由两种方法。

    • 自上而下的递归
      大问题分成若干子问题之后,对于每个子问题分别求解,求解的方式跟求解原问题的方式一样,所以产生了递归。由于所有的递归方法都可以用迭代重写,所以就有了第二种方法。
void mergeSort(T *a, int left, int right)
{
	// 对数组元素a[left, right]进行排序
	if (left < right)
	{
		// 至少有两个元素
		int middle = (left + right) / 2;
		mergeSort(a, left, middle);
		mergeSort(a, middle + 1, right);
		merge(a, b, left, middle, right); // 从a到b归并
		copy(b, a, left, right); // 将排序结果复制到a
	}
}

void Merge(int arr[], int left, int middle, int right)
 {  //归并操作
     
     int length = right - left + 1;
     int *pArr;
     int beginA = left,beginB = middle + 1;  //设置两个标志,分别指向两个已排序序列的起始位置
     int nCount = 0,i;
     
     pArr = (int *)malloc(sizeof(int)*length);  //开辟空间
     if (pArr == NULL)
     {
         printf("malloc error\n");
         return;
     }
     
     while (beginA <= middle)
     {
         if (arr[beginA] > arr[beginB])
         {
             pArr[nCount++] = arr[beginB++];
         }
         if (arr[beginA] < arr[beginB])
         {
             pArr[nCount++] = arr[beginA++];
         }
         if (beginB > right) break;
     }
     
     while (beginA <= middle)
     {
         pArr[nCount++] = arr[beginA++];
     }
     
     while (beginB <= right)
     {
         pArr[nCount++] = arr[beginB++];
     }
     
     for (i = 0; i < length; i++)   //把排序好的部分移回arr数组中
     {
         arr[left++] = pArr[i];
     }
     free(pArr);
 }

- 自下而上的迭代
void mergeSort(T a[], int n)
{
	T *b = new T [n];
	int segmentSize = 1;
	while(segmentSize < n)
	{
		mergePass(a, b, n, segmentSize);
		segmentSize += segmentSize;
		mergePass(b, a, n, segmentSize);
		segmentSize += segmentSize;
	}
	delete[] b;
}

void mergePass(T)
  1. 稳定排序和非稳定排序,
  2. 二叉搜索树的递归定义

算法题 股票买卖一次交易

风控策略算法工程师:

1、NC62平衡二叉树

服务端测试开发工程师:

1、NC20数字字符串转化成IP地址

多模态算法工程师:

1、NC25删除有序链表中重复的元素

2、NC88寻找第K大

测试开发工程师:

1、NC4判断链表中是否有环

2、NC91最长递增子序列

3、NC28最小覆盖子串

(动态规划问题)
过河问题

小明需要踩着石头过河。
下一步只能到达距离为3、4、5石头。
给一个数组,里面是n个石头,以及石头到岸边的距离。
假设从小到大排序。

图卷积涉猎

GCN的原理
笔者从直觉角度(特征聚合,特征变换)回答,这个角度与空域图卷积非常类似(实际上GCN有着充分的理论基础保证,所以还应该从谱图卷积的角度回答,详见问题5)

GCN有什么问题
笔者仍然从 Over smoothing,梯度消失等方面回答
5.如何从标准的谱图卷积过渡到现在的典型GCN。
谱图卷积——切比雪夫多项式近似频域卷积核——取一阶近似并对切比雪夫系数进行化简

强化学习涉猎

Q-Learning和DQN的区别?
这个是强化学习的问题,笔者只了解过一些经典强化学习算法,并未真正参与过相关项目。
DQN使用一个NN代替Q-Learning中的Q表,Q-Learning无法处理某些状态空间S非常大的场景(Q表太大,无法存储和更新),所以使用神经网络代替Q表输出Q(s, a)。

常用深度学习算法介绍
概述

项目中用到的算法
目标检测
目标分割
目标跟踪

风控相关

一面:

问了道概率题,患病概率0.001,患病中检测出的概率为0.95,未患病中检测出的概率为0.05,问检测出的患病概率为多少?
后验概率是(0.0010.95)/(0.0010.95+0.05*0.999)
evidence也是P(检测),后验概率P(有病|检测)就是等于P(检测|有病)*P(有病)/(P(检测|有病)*P(有病)+P(检测|没病)*P(没病))

附录:

编辑距离回溯代码

#pragma once
#include <vector>
#include <bits/stdc++.h>
using namespace std;
#define ov "no_temp"
#define _n "blank"

template<class T>
class Distance {
public:
    typedef pair<int, vector<char>> fNode;
    typedef pair<vector<T>,vector<T>> respair;
    Distance(const vector<T>& temp, const vector<T>& fact,int cost_del = 1, int cost_ins = 2, int cost_err = 1, int cost_dai = 0);
    vector<respair> traceback(int i , int j);
    vector<respair> traceback();
    int getDis();
    void print_aligns(vector<string> & a,vector<string>& b,int mode = 0);
private:
    vector<T> temp;
    vector<T> fact;
    vector<vector<fNode>> table;
};

template<class T>
Distance<T>::Distance(const vector<T> &temp, const vector<T> &fact, int cost_del, int cost_ins, int cost_err,int cost_dai):
        temp(temp),fact(fact)
{
    int n1 = temp.size() + 1;
    int n2 = fact.size() + 1;
    int dis[3];
    table.resize(n1);
    for(int i = 0; i < n1; i ++)
    {
        table[i].resize(n2);
    }
    for (int i = 1; i < n1; i++) table[i][0] = pair<int, vector<char>>(i, {'_'});
    for (int i = 1; i < n2; i++) table[0][i] = pair<int, vector<char>>(i, {'|'});
    for (int i = 1; i < n1; i++)
        for (int j = 1; j < n2; j++)
        {
            dis[0] = table[i - 1][j].first + cost_del;
            dis[1] = table[i][j - 1].first + cost_ins;
            dis[2] = table[i - 1][j - 1].first + cost_err;
            if(temp[i - 1] == fact[j - 1] || temp[i - 1] == ov)dis[2] = table[i - 1][j - 1].first;
            else dis[2] = table[i - 1][j - 1].first + cost_err;
            auto min = *min_element(dis,dis+3);
            table[i][j].first = min;
            if(dis[0] == min) table[i][j].second.emplace_back('_');
            if(dis[1] == min) table[i][j].second.emplace_back('|');
            if(dis[2] == min) table[i][j].second.emplace_back('/');
        }
}

template<class T>
vector<typename Distance<T>::respair> Distance<T>::traceback(int i, int j)
{
    if(i == 0 && j == 0){
        return vector<respair> {respair {}};
    }
    vector<respair> ret;
    for(auto c : table[i][j].second)
    {
        if(c == '|')
        {
            for(auto p : traceback(i,j-1))
            {
                vector<T> a,b;
                a.insert(a.end(),p.first.begin(),p.first.end());
                a.emplace_back(_n);
                b.insert(b.end(),p.second.begin(),p.second.end());
                b.emplace_back(fact[j - 1]);
                ret.emplace_back(respair {a,b});
            }
        }
        if(c == '/')
        {
            for(auto p : traceback(i-1,j-1))
            {
                vector<T> a,b;
                a.insert(a.end(),p.first.begin(),p.first.end());
                a.emplace_back(temp[i - 1]);
                b.insert(b.end(),p.second.begin(),p.second.end());
                b.emplace_back(fact[j - 1]);
                ret.emplace_back(respair {a,b});
            }
        }
        if(c == '_')
        {
            for(auto p : traceback(i-1,j))
            {
                vector<T> a,b;
                a.insert(a.end(),p.first.begin(),p.first.end());
                a.emplace_back(temp[i - 1]);
                b.insert(b.end(),p.second.begin(),p.second.end());
                b.emplace_back(_n);
                ret.emplace_back(respair {a,b});
            }
        }

    }
    return ret;
}

template<class T>
vector<typename Distance<T>::respair> Distance<T>::traceback()
{
    int i = temp.size();
    int j = fact.size();
    if(i == 0 && j == 0){
        return vector<respair> {respair {}};
    }
    vector<respair> ret;
    for(auto c : table[i][j].second)
    {
        if(c == '|')
        {
            for(auto p : traceback(i,j-1))
            {
                vector<T> a,b;
                a.insert(a.end(),p.first.begin(),p.first.end());
                a.emplace_back(_n);
                b.insert(b.end(),p.second.begin(),p.second.end());
                b.emplace_back(fact[j - 1]);
                ret.emplace_back(respair {a,b});
            }
        }
        if(c == '/')
        {
            for(auto p : traceback(i-1,j-1))
            {
                vector<T> a,b;
                a.insert(a.end(),p.first.begin(),p.first.end());
                a.emplace_back(temp[i - 1]);
                b.insert(b.end(),p.second.begin(),p.second.end());
                b.emplace_back(fact[j - 1]);
                ret.emplace_back(respair {a,b});
            }
        }
        if(c == '_')
        {
            for(auto p : traceback(i-1,j))
            {
                vector<T> a,b;
                a.insert(a.end(),p.first.begin(),p.first.end());
                a.emplace_back(temp[i - 1]);
                b.insert(b.end(),p.second.begin(),p.second.end());
                b.emplace_back(_n);
                ret.emplace_back(respair {a,b});
            }
        }

    }
    return ret;
}

template<class T>
int Distance<T>::getDis() {
    return table[temp.size()][fact.size()].first;
}

template<class T>
void Distance<T>::print_aligns(vector<string> & a,vector<string>& b,int mode) {
    vector<Distance<string>::respair> res = this->traceback();
    cout << "min dis:" << this->getDis() << endl;
    if(mode == 0)
    {
        for(const auto& r : res)
        {
            for(const auto& c : r.first){
                if(c == _n)
                {
                    a.emplace_back("_");
                    continue;
                }
                a.emplace_back(c);
            }
            for(const auto& c : r.second){
                if(c == _n)
                {
                    b.emplace_back("_");
                    continue;
                }
                b.emplace_back(c);
            }
        }
    } else
    {
        for(int i = 0; i < mode && i < res.size(); i++)
        {
            auto r = res[i];
            for(const auto& c : r.first){
                if(c == _n)
                {
                    a.emplace_back("_");
                    continue;
                }
                a.emplace_back(c);
            }
            for(const auto& c : r.second){
                if(c == _n)
                {
                    b.emplace_back("_");
                    continue;
                }
                b.emplace_back(c);
            }
        }
    }

}

字节跳动校招面试精髓

https://www.nowcoder.com/discuss/373163

MD5算法简介

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值