【数据结构】基于BF算法的树种病毒检测

任务描述

双生病毒是一类世界范围内广泛存在的单链环状DNA病毒,对多种植物造成毁灭性危害。近年来,随着技术的发展,研究人员不断从苹果、桑树等多种多年生树种中检测到新的双生病毒,并将其命名为苹果双生病毒(AGV)和桑花叶萎缩相关病毒(MMDaV)。

为了快速检测出树种是否感染某种双生病毒,将树种的DNA和病毒DNA均表示成由一些字母组成的字符串序列,然后检测某种双生病毒的DNA序列是否在该树种的DNA序列中出现过,如果出现过,则此树种感染了病毒,否则没有感染。注意,树种的DNA序列是线性的,而病毒的DNA序列是环状的。

使用BF算法检测树种是否感染双生病毒,若感染,则输出"YES",否则输出"No"。例如,双生病毒的DNA序列为baa,树种1的DNA序列为aaabbba,树种2的DNA序列为babbba,则可以判断树种1感染了双生病毒,而树种2未感染。

编程要求

输入

多组数据,每组数据有一行,为序列A和B,A对应病毒的DNA序列,B对应树木的DNA序列。A和B都为"0"时输入结束。

输出

对于每组数据输出一行,若树木感染了病毒输出"YES",否则输出"NO"。

输入样例:

在这里给出一组输入。例如:

复制代码
baa aaabbba
baa babbba
abbab abbabaab
baa aaababa
bc acbe
0 0

输出样例:

在这里给出相应的输出。例如:

bash 复制代码
YES
NO
YES
YES
YES

算法步骤:

1. 数据结构定义

使用HString结构体存储字符串,包含:

  • ch[600]:字符数组(从索引 1 开始存储,方便后续匹配逻辑);
  • len:字符串长度。

2. 核心函数功能与步骤

(1)暴力匹配算法(Index_BF
  • 作用:在主串S中从第pos个字符开始,查找模式串T第一次出现的位置。
  • 步骤:
    • 初始化主串指针i = pos(1-based)、模式串指针j = 1(1-based);
    • 若当前字符匹配(S.ch[i] == T.ch[j]),则双指针同时后移(i++j++);
    • 若不匹配,主串指针回溯到 "上次匹配起始位置 + 1"(i = i - j + 2),模式串指针重置(j = 1);
    • 若模式串遍历完毕(j > T.len),返回匹配起始位置(i - T.len);否则返回 0(无匹配)。
(2)环状序列倍增(DoubleCircularSequence
  • 作用:将环状序列(如病毒 DNA)转换为线性序列,便于提取所有旋转形式。
  • 步骤:
    • 设原序列长度为m,将原序列复制一份拼接到自身后面(如 "baa"→"baabaa");
    • 更新序列长度为2*m,此时所有旋转形式都是该倍增序列中长度为m的子串。
(3)匹配环状模式串(MatchCircularPattern
  • 作用:检查主串(树木 DNA)中是否包含模式串(病毒 DNA)的任意旋转形式。
  • 步骤:
    • 从倍增后的病毒串中,提取所有长度为m(原病毒长度)的子串(每个子串对应一种旋转形式);
    • 对每个子串,调用Index_BF在树木 DNA 中查找;
    • 若找到任一匹配,返回匹配位置(非 0);否则返回 0。
(4)主流程(Virus_detection
  • 步骤:
    • 循环读取输入的病毒 DNA(temp1)和树木 DNA(temp2),直到输入 "0 0" 终止;
    • 将输入字符串存入HString结构(从索引 1 开始存储),并计算长度;
    • 对病毒 DNA 调用DoubleCircularSequence进行倍增处理;
    • 调用MatchCircularPattern检查树木 DNA 中是否包含病毒 DNA 的任意旋转;
    • 若存在匹配,输出 "YES";否则输出 "NO"。

C++代码:

cpp 复制代码
#include<iostream>
#include<string.h>
using namespace std;
typedef struct{   
   char ch[600];				//若是非空串,则按串长分配存储区,否则ch为NULL   
   int len;				//串长度   
}HString;
int Index_BF(HString S,HString T,int pos);
void DoubleCircularSequence(HString &str);//将环状序列倍增,便于线性处理所有旋转情况
int MatchCircularPattern(HString main, HString doubled_pattern);//在主串中查找环状模式串的所有可能旋转(假设pattern已倍增)
void Virus_detection();
int main()
{
	Virus_detection();
	return 0;
}
int Index_BF(HString S,HString T,int pos)
{//返回模式T在主串S中第pos个字符开始第一次出现的位置。若不存在,则返回值为0
 //其中,T非空,1≤pos≤StrLength(S)
	int i,j;
   i=pos; j=1;
   while(i<=S.len&&j<=T.len)
   {
      if(S.ch[i]==T.ch[j]){++i;++j;}	//继续比较后继字符
      else{i=i-j+2;j=1;}	//指针后退重新开始匹配
   }
   if(j>T.len) return i-T.len;
   else return 0;
}
void DoubleCircularSequence(HString &str)
{//将环状序列倍增,便于线性处理所有旋转情况
   int m = str.len;
   int i, j;
   for(i=m+1, j=1; j<=m; j++)
      str.ch[i++] = str.ch[j];
   str.ch[2*m+1] = '\0';  //添加结束符号
   str.len = 2*m;         //更新长度为倍增后的长度
}

int MatchCircularPattern(HString main, HString doubled_pattern)
{//在主串中查找环状模式串的所有可能旋转(假设pattern已倍增)
   int m = doubled_pattern.len / 2;  //从倍增后的长度计算原始长度
   int flag = 0;
   HString temp;

   // 遍历所有可能的旋转情况
   for(int i=0; i<m; i++)
   {
      // 提取长度为m的旋转子串
      for(int j=1; j<=m; j++)
         temp.ch[j] = doubled_pattern.ch[i+j];
      temp.ch[m+1] = '\0';
      temp.len = m;  //提取的子串长度就是m

      // 进行模式匹配
      flag = Index_BF(main, temp, 1);
      if(flag) break;  // 找到匹配立即返回
   }
   return flag;
}
void Virus_detection()
{
   int flag;
   char temp1[600], temp2[600];  // 临时数组用于接收输入
   HString Virus,Tree;
   while(cin>>temp1>>temp2) //依次检测每对病毒DNA和树木DNA是否匹配
   {
      //判断终止条件
      if(strcmp(temp1,"0")==0 && strcmp(temp2,"0")==0) break;

      //将临时数组内容复制到HString结构(从索引1开始)
      strcpy(Virus.ch+1, temp1);
      strcpy(Tree.ch+1, temp2);
	  Virus.len=strlen(Virus.ch+1);  //从ch[1]开始计算长度
	  Tree.len=strlen(Tree.ch+1);

	  //环状序列匹配处理
	  DoubleCircularSequence(Virus);      //倍增处理
	  flag=MatchCircularPattern(Tree,Virus); //环状序列匹配
      if(flag)
         cout<<"YES"<<endl;
      else
         cout<<"NO"<<endl;
   }//while
}

Python代码:

python 复制代码
def index_bf(S, T, pos):
    """
    暴力匹配算法(还原C++逻辑)
    :param S: 主串(树木DNA,0-based字符串)
    :param T: 模式串(病毒DNA的某个旋转,0-based字符串)
    :param pos: 匹配起始位置(1-based)
    :return: 匹配成功返回1-based起始位置,失败返回0
    """
    len_S = len(S)
    len_T = len(T)
    # 非法输入判断(模式串为空或起始位置越界)
    if len_T == 0 or pos < 1 or pos > len_S:
        return 0
    i = pos - 1  # 转换为主串0-based索引
    j = 0        # 模式串0-based索引
    while i < len_S and j < len_T:
        if S[i] == T[j]:
            i += 1
            j += 1
        else:
            # 回溯:主串回到上次匹配起始位置+1,模式串重置
            i = i - j + 1
            j = 0
    # 模式串完全匹配,返回1-based起始位置
    if j == len_T:
        return i - len_T + 1
    # 匹配失败
    return 0


def double_circular_sequence(s):
    """将环状序列倍增(还原C++逻辑),如"baa"→"baabaa",覆盖所有旋转情况"""
    return s + s


def match_circular_pattern(main_str, doubled_pattern):
    """
    匹配环状模式串的所有旋转形式
    :param main_str: 主串(树木DNA)
    :param doubled_pattern: 倍增后的模式串(病毒DNA)
    :return: 存在匹配返回True,否则返回False
    """
    m = len(doubled_pattern) // 2  # 病毒DNA原始长度
    # 遍历所有旋转情况(提取倍增串中长度为m的子串)
    for i in range(m):
        pattern = doubled_pattern[i:i+m]
        # 调用暴力匹配,从主串第1个字符开始
        if index_bf(main_str, pattern, 1) > 0:
            return True
    return False


def virus_detection():
    """主函数:读取输入并检测病毒DNA"""
    while True:
        # 读取一行输入并分割(处理可能的空行和多空格)
        line = input().strip()
        while not line:  # 跳过空行
            line = input().strip()
        temp1, temp2 = line.split()
        # 终止条件:两个输入均为"0"
        if temp1 == "0" and temp2 == "0":
            break
        # 处理病毒串和树木串
        virus = temp1
        tree = temp2
        # 倍增病毒串(生成所有旋转的候选子串)
        doubled_virus = double_circular_sequence(virus)
        # 检测是否存在匹配的旋转形式
        if match_circular_pattern(tree, doubled_virus):
            print("YES")
        else:
            print("NO")


if __name__ == "__main__":
    virus_detection()

Java代码:

java 复制代码
import java.util.Scanner;

// 模拟C++中的HString结构体
class HString {
    char[] ch = new char[600]; // 字符数组,从索引1开始存储
    int len; // 串长度
}

public class VirusDetection {

    // 暴力匹配算法:返回模式串T在主串S中从pos位置开始的第一次出现位置(1-based),不存在返回0
    public static int indexBF(HString S, HString T, int pos) {
        int i = pos; // 主串指针(1-based)
        int j = 1; // 模式串指针(1-based)
        while (i <= S.len && j <= T.len) {
            if (S.ch[i] == T.ch[j]) {
                i++;
                j++;
            } else {
                // 指针回溯:主串回到上次匹配起始位置+1,模式串重置
                i = i - j + 2;
                j = 1;
            }
        }
        if (j > T.len) {
            return i - T.len; // 匹配成功,返回起始位置
        } else {
            return 0; // 匹配失败
        }
    }

    // 将环状序列倍增(便于处理所有旋转情况)
    public static void doubleCircularSequence(HString str) {
        int m = str.len;
        // 将原序列复制到后面,形成倍增序列(1..m 复制到 m+1..2m)
        for (int i = m + 1, j = 1; j <= m; j++) {
            str.ch[i++] = str.ch[j];
        }
        str.ch[2 * m + 1] = '\0'; // 结束符(Java中可不显式处理,但保持逻辑一致)
        str.len = 2 * m; // 更新长度为倍增后的值
    }

    // 在主串中查找环状模式串的所有可能旋转(假设模式串已倍增)
    public static int matchCircularPattern(HString main, HString doubledPattern) {
        int m = doubledPattern.len / 2; // 原始模式串长度
        HString temp = new HString();
        temp.len = m;

        // 遍历所有可能的旋转(提取倍增串中长度为m的子串)
        for (int i = 0; i < m; i++) {
            // 构建当前旋转的模式串(从i+1位置开始取m个字符)
            for (int j = 1; j <= m; j++) {
                temp.ch[j] = doubledPattern.ch[i + j];
            }
            temp.ch[m + 1] = '\0';

            // 调用暴力匹配,从主串第1个字符开始查找
            int flag = indexBF(main, temp, 1);
            if (flag != 0) {
                return flag; // 找到匹配则返回
            }
        }
        return 0; // 未找到匹配
    }

    // 病毒检测主函数
    public static void virusDetection() {
        Scanner scanner = new Scanner(System.in);
        while (true) {
            String temp1 = scanner.next();
            String temp2 = scanner.next();

            // 终止条件:输入为"0 0"
            if (temp1.equals("0") && temp2.equals("0")) {
                break;
            }

            HString virus = new HString();
            HString tree = new HString();

            // 将输入字符串复制到HString(从索引1开始存储)
            for (int i = 0; i < temp1.length(); i++) {
                virus.ch[i + 1] = temp1.charAt(i);
            }
            virus.len = temp1.length();

            for (int i = 0; i < temp2.length(); i++) {
                tree.ch[i + 1] = temp2.charAt(i);
            }
            tree.len = temp2.length();

            // 处理环状序列并匹配
            doubleCircularSequence(virus);
            int flag = matchCircularPattern(tree, virus);

            // 输出结果
            if (flag != 0) {
                System.out.println("YES");
            } else {
                System.out.println("NO");
            }
        }
        scanner.close();
    }

    public static void main(String[] args) {
        virusDetection();
    }
}
相关推荐
little_xianzhong2 小时前
三个常听到的消息/中间件MQTT RabbitMQ Kafka
java·笔记·中间件·消息队列
论迹2 小时前
【Spring Cloud 微服务】-- 服务拆分原则
java·spring cloud·微服务
汤姆yu2 小时前
基于springboot的民间救援队救助系统
java·spring boot·后端·救援队
闭着眼睛学算法2 小时前
【双机位A卷】华为OD笔试之【哈希表】双机位A-采购订单【Py/Java/C++/C/JS/Go六种语言】【欧弟算法】全网注释最详细分类最全的华子OD真题题解
java·华为od·散列表
蒙奇D索大2 小时前
【算法】递归算法实战:汉诺塔问题详解与代码实现
c语言·考研·算法·面试·改行学it
图灵信徒2 小时前
R语言绘图与可视化第六章总结
python·数据挖掘·数据分析·r语言
封奚泽优2 小时前
使用Labelme进行图像标注
开发语言·python·labelme
勿忘,瞬间2 小时前
Maven
java·maven
檐下翻书1732 小时前
智能医疗大模型在医生培训中的应用案例
python