LeetCode - 28 找出字符串中第一个匹配项的下标

题目来源

28. 找出字符串中第一个匹配项的下标 - 力扣(LeetCode)

题目解析

暴力解法

本题如果采用暴力解法的话,可以定义两个指针 i,j,其中 i 指针用于扫描 S(haystack)串,j 指针用于扫描 T(needle)串。

比如:S = "aabaabaaf",T = "aabaaf"

假设 S 串的起始匹配位置为 k,则 k 取值范围是:[0, s.length - t.length]

上图匹配过程中,分为两个循环:

外层循环,即匹配的轮数控制,或者说是,S串的匹配起始位置控制,比如:

  • 第 0 轮,T 串是从 S 串的 0 索引位置开始匹配
  • 第 1 轮,T 串是从 S 串的 1 索引位置开始匹配
  • ...
  • 第 k 轮,T 串是从 S 串的 k 索引位置开始匹配

内层循环,即T串和S串的 k ~ k + t.length 范围进行逐个字符一一匹配,

  • 如果发现存在对应位的字符不一致,则说明当前轮匹配失败,直接进入下一轮
  • 如果所有位置上的字符都相同,则说明匹配成功,即在S中找到了和T相同的子串,且该子串起始位置是k

假设,s.length = n,t.length = m,则暴力解法的时间复杂度为O(n * m)

KMP算法

对于字符串模式匹配问题,暴力算法并非最优解决方案,虽然 s,t 都是随机串,但是这些随机串也会存在一定规律可以利用。

比如上面暴力解法图示中,当第 k = 0 轮匹配失败后,第 k =1 轮,第 k =2 轮是否注定失败了呢?

如下图是第 k = 0 轮最后一个字符匹配失败的情况:

我们观察其中匹配成功的部分,即"aabaa"部分,这部分具有相同前后缀aa

如果我们将 S,T 的 "aabaa" 后面部分抽象化(....),如下图所示,那么:

  • 第 k = 0 轮匹配失败是因为 “抽象部分(....)” 的匹配失败

  • 第 k = 1 轮,第 k = 2 轮匹配失败,其实就是 "aabaa" 部分的匹配失败:

 我们将第 k = 1 轮,第 k = 2 轮,第 k = 3 轮再次简化一下,如下图所示:

那么是不是很显然可以发现,第1轮,第2轮是注定失败的。

我们再举一个例子:

如果上面 S,T 在第 k = 0 轮因为抽象部分(...)匹配失败,那么下一轮,其实是否可以直接让:前缀部分直接跳转到后缀位置

因为前缀部分(如abc)和后缀部分(如abc)完全相同,而前缀部分(如abc)和中间部分(如d)不相同,因此前缀部分(如abc)和中间部分对齐(如d)时,必然匹配失败。

这样的话,是不是跳过了两轮匹配,即节省了两轮匹配的时间。

请大家再思考一下,上面让前缀部分直接跳转和后缀部分对齐,真的是只节省两轮匹配的过程吗?

下面图示是,第0轮匹配失败后,直接跳到对称部分开始重新匹配

如果对应到暴力解法过程的话,那么下面画X的部分就都是跳过的过程

我们再观察下这个跳到对称部分的过程中,i,j指针的变化

可以发现,i 指针在 S 中的位置并没有改变

  • j 指针回退指向到了 T 的 "aabaa" 前缀部分(aa)的后一个位置(b所在位置)
  • 或者假设前缀部分(aa)长度为 len,则 j 回退到 T 串的 len 索引位置

那么上面这个改进算法的时间复杂度是多少呢?

由于上面算法中,保证了 i 指针不会回退,因此时间复杂度只有O(n)。

而这个算法其实就是KMP算法。

前缀表概念

上面我们已经说明了KMP算法的大致原理,其中最关键的就是在模式串 T 中找其前缀子串的最长相同前后缀,比如

T = "aabaaf" 有前缀子串 "aabaa",该子串的最长相同前后缀是 "aa"

那么该如何通过代码来实现这个功能呢?

KMP算法的三个创始人K,M,P提出了前缀表的概念。

首先定义下字符串的前缀、后缀概念

假设字符串 t 长度为n,那么:

  • 前缀就是起始索引必须为0,结束索引<n-1的所有子串
  • 后缀就是结束索引必须为n-1,起始索引必须>0的所有子串

因此

  • 前缀和后缀不能是字符串 t 本身
  • 字符串 t 的前缀和后缀是可能存在重叠部分的

我们举一个例子,比如列出T的子串 "aabaa" 的所有的前缀和后缀

长度前缀(红色子串)后缀(绿色子串)
1aabaaaabaa
2aabaaaabaa
3aabaaaabaa
4aabaaaabaa

其中最长且相同的前后缀是"aa"。

注意,判断前缀和后缀是否相同,都是从左往右逐一比对,因此上面例子中,长度为3的前缀"aab"和后缀"baa"是不相同的。

还有相同的前缀、后缀是可能存在重叠

比如字符串 "ababab"

长度前缀(红色子串)后缀(绿色子串)
1abababababab
2abababababab
3abababababab
4abababababab
5abababababab

最长相同的前缀和后缀是"abab" ,他们是存在重叠的

因此T = "aabaaf"所有前缀子串的最长相同的前缀和后缀的长度分别为:

T的前缀串最长相同的前后缀最长相同的前后缀的长度
a""0
aaa1
aab""0
aabaa1
aabaaaa2
aabaaf""0

我们将 T 的所有前缀串对应的 “最长相同的前后缀的长度” 记录为一个数组 next,我们称 next 为前缀表

next = [0, 1, 0, 1, 2, 0]

    前缀表的应用

    前面我们手算出了前缀表 next 数组

    next = [0, 1, 0, 1, 2, 0]

    next[j] 表示:T 的 [0, j] 范围子串的最长相同前后缀长度,比如:

    • next[0] 表示:T的 [0,0] 范围子串 "a"          的最长相同前后缀长度 0
    • next[1] 表示:T的 [0,1] 范围子串 "aa"        的最长相同前后缀长度 1
    • next[2] 表示:T的 [0,2] 范围子串 "aab"      的最长相同前后缀长度 0
    • next[3] 表示:T的 [0,3] 范围子串 "aaba"    的最长相同前后缀长度 1
    • next[4] 表示:T的 [0,4] 范围子串 "aabaa"  的最长相同前后缀长度 2
    • next[5] 表示:T的 [0,5] 范围子串 "aabaaf" 的最长相同前后缀长度 0

    那么如何将 next 应用到KMP算法中呢?

    比如下图中,S[i] != T[j] 时,我们前面分析过,需要做如下动作:

    • i 指针保持指向不变
    • j 指针回退到 T 的 len 索引位置(len:表示 T 的前缀串 "aabaa" 的最长相同前后缀 "aa" 的长度)

    len 含义和 next[j-1] 含义是相同的

    • next[j - 1] 表示 T 的 [0, j-1] 范围子串 "aabaa" 的最长相同前后缀的长度 

    因此,当s[i] != t[j] 时,我们可以让: j = next[ j - 1 ]

    另外,如果 j = 0 时就匹配不上,此时 next[j-1] 会发生越界异常,因此针对这种i情况,我们应该特殊处理,如下图所示,就是一个 j = 0无法匹配的情况:

    此时,我们应该让 i++,j 保持不变,继续匹配

    这其实和前面KMP算法规定的 i 指针不回退这一条件不冲突。因为上面过程 i 指针没有发生回退。

    生成前缀表

    前面我们已经手算过了前缀表,但是手算过程是一个暴力枚举的过程。

    关于前缀表的生成,我们可以利用动态规划求解。

    比如:假设已知 K = NEXT[J-1],现在要求 NEXT[J],比如下图

    如果 T[J] == T[K] 的话,比如

    NEXT[J] 表示 [0, J] 范围子串 "abdabeabdabe" 的最长相同前后缀("abdabe")长度为 K+1

    因此当 T[J] == T[K] 时,那么 NEXT[J]  = K + 1

    如果T[J] ! = T[K]的话,比如

    那么此时该如何求解 NEXT[J] 呢?

    我们将上图继续分解

    如果下面两个位置的字符相同

    那么此时就找到了 NEXT[J] 的值。

    因此当 T[J] != T[K] 时,我们可以让 K = NEXT[K-1],继续前面逻辑,直到 K 移动到:

    • T[J] == T[K] 时,此时 NEXT[J] = K + 1
    • 若 K == 0 时,依旧 T[J] != T[K],则此时可以认为 NEXT[J] = 0,此时我们J++,求解下一个 NEXT[J]

    C算法源码

    暴力解法
    int strStr(char* s, char* t) {int sLen = strlen(s);int tLen = strlen(t);for (int k = 0; k <= sLen - tLen; k++) {int i = k;int j = 0;while (j < tLen && s[i] == t[j]) {i++;j++;}if (j == tLen) {return k;}}return -1;
    }
    KMP算法
    int* getNext(char* t) {int tLen = strlen(t);int* next = (int*)calloc(tLen, sizeof(int));int j = 1;int k = next[j - 1];while (j < tLen) {if (t[j] == t[k]) {next[j] = k + 1; // 前缀范围 t[0,k] == 后缀范围 t[j-k,j]  此时最长相同前后缀长度为:k+1j++;k++;} else if (k > 0) {k = next[k - 1]; // 若 t[j] != t[k] && k > 0,则缩短前缀部分,k = next[k-1] 后继续比较 t[k] 和 t[j]} else {j++; // 若 t[j] != t[k] && k == 0,则 k 无法继续后退,此时可以认为 next[j] 为 0,进行j++,继续求解下一个next[j]}}return next;
    }int strStr(char* s, char* t) {int sLen = strlen(s);int tLen = strlen(t);int* next = getNext(t); // 生成 t 串的前缀表int i = 0;int j = 0;while (i < sLen && j < tLen) {if (s[i] == t[j]) {i++;j++;} else if (j > 0) {j = next[j - 1]; // 若 s[i] != t[j] && j > 0,则 i 指针不动,j 指针回退到 t 串的 next[j-1] 位置 } else {i++; // 若 s[i] != t[j] && j == 0,则表示 s[i] 和 t[0] 首个字符就匹配失败,我们只能进入下一轮,即 i++}}if (j == tLen) { // 若 t 串所有字符都被匹配成功,则 j == t.lengthreturn i - j; // 此时 s 串的 i - j 位置就是首次匹配 t 的子串起始位置} else {return -1;}
    }

    C++算法源码

    暴力解法
    class Solution {
    public:int strStr(string s, string t) {int sLen = s.size();int tLen = t.size();for (int k = 0; k <= sLen - tLen; k++) {int i = k;int j = 0;while (j < tLen && s[i] == t[j]) {i++;j++;}if (j == tLen) {return k;}}return -1;}
    };
    KMP算法
    class Solution {
    public:int strStr(string s, string t) {vector<int> next = getNext(t); // 生成 t 串的前缀表int i = 0;int j = 0;while (i < s.size() && j < t.size()) {if (s[i] == t[j]) {i++;j++;} else if (j > 0) {j = next[j - 1]; // 若 s[i] != t[j] && j > 0,则 i 指针不动,j 指针回退到 t 串的 next[j-1] 位置 } else {i++; // 若 s[i] != t[j] && j == 0,则表示 s[i] 和 t[0] 首个字符就匹配失败,我们只能进入下一轮,即 i++}}if (j == t.size()) { // 若 t 串所有字符都被匹配成功,则 j == t.lengthreturn i - j; // 此时 s 串的 i - j 位置就是首次匹配 t 的子串起始位置} else {return -1;}}vector<int> getNext(string t) {vector<int> next(t.size(), 0);int j = 1;int k = next[j - 1];while (j < t.size()) {if (t[j] == t[k]) {next[j] = k + 1;  // 前缀范围 t[0,k] == 后缀范围 t[j-k,j]  此时最长相同前后缀长度为:k+1j++;k++;} else if (k > 0) {k = next[k - 1]; // 若 t[j] != t[k] && k > 0,则缩短前缀部分,k = next[k-1] 后继续比较 t[k] 和 t[j]} else {j++; // 若 t[j] != t[k] && k == 0,则 k 无法继续后退,此时可以认为 next[j] 为 0,进行j++,继续求解下一个next[j]}}return next;}
    };

    Java算法源码

    暴力解法
    
    class Solution {public int strStr(String haystack, String needle) {char[] s = haystack.toCharArray();char[] t = needle.toCharArray();for (int k = 0; k <= s.length - t.length; k++) {int i = k;int j = 0;while (j < t.length && s[i] == t[j]) {i++;j++;}if (j == t.length) {return k;}}return -1;}
    }
    KMP算法
    
    class Solution {public int strStr(String haystack, String needle) {char[] s = haystack.toCharArray();char[] t = needle.toCharArray();int[] next = getNext(t); // 生成 t 串的前缀表int i = 0;int j = 0;while (i < s.length && j < t.length) {if (s[i] == t[j]) {i++;j++;} else if (j > 0) { // 若 s[i] != t[j] && j > 0,则 i 指针不动,j 指针回退到 t 串的 next[j-1] 位置 j = next[j - 1];} else { // 若 s[i] != t[j] && j == 0,则表示 s[i] 和 t[0] 首个字符就匹配失败,我们只能进入下一轮,即 i++i++;}}if (j == t.length) { // 若 t 串所有字符都被匹配成功,则 j == t.lengthreturn i - j; // 此时 s 串的 i - j 位置就是首次匹配 t 的子串起始位置} else {return -1;}}public static int[] getNext(char[] t) {int[] next = new int[t.length];int j = 1;int k = next[j-1];while (j < t.length) {if (t[j] == t[k]) {next[j] = k + 1; // 前缀范围 t[0,k] == 后缀范围 t[j-k,j]  此时最长相同前后缀长度为:k+1j++;k++;} else if (k > 0) { // 若 t[j] != t[k] && k > 0,则缩短前缀部分,k = next[k-1] 后继续比较 t[k] 和 t[j]k = next[k - 1];} else {j++; // 若 t[j] != t[k] && k == 0,则 k 无法继续后退,此时可以认为 next[j] 为 0,进行j++,继续求解下一个next[j]}}return next;}
    }

    Python算法源码

    暴力解法
    class Solution(object):def strStr(self, s, t):""":type haystack: str:type needle: str:rtype: int"""for k in range(0, len(s) - len(t) + 1):i = kj = 0while j < len(t) and s[i] == t[j]:i += 1j += 1if j == len(t):return kreturn -1
    KMP算法
    def getNext(t):next = [0] * len(t)j = 1k = next[j - 1]while j < len(t):if t[j] == t[k]:next[j] = k + 1  # 前缀范围 t[0,k] == 后缀范围 t[j-k,j]  此时最长相同前后缀长度为:k+1j += 1k += 1elif k > 0:k = next[k - 1]  # 若 t[j] != t[k] && k > 0,则缩短前缀部分,k = next[k-1] 后继续比较 t[k] 和 t[j]else:j += 1  # 若 t[j] != t[k] && k == 0,则 k 无法继续后退,此时可以认为 next[j] 为 0,进行j++,继续求解下一个next[j]return nextclass Solution(object):def strStr(self, s, t):""":type haystack: str:type needle: str:rtype: int"""next = getNext(t)  # 生成 t 串的前缀表i, j = 0, 0while i < len(s) and j < len(t):if s[i] == t[j]:i += 1j += 1elif j > 0:j = next[j - 1]  # 若 s[i] != t[j] && j > 0,则 i 指针不动,j 指针回退到 t 串的 next[j-1] 位置 else:i += 1  # 若 s[i] != t[j] && j == 0,则表示 s[i] 和 t[0] 首个字符就匹配失败,我们只能进入下一轮,即 i++if j == len(t):  # 若 t 串所有字符都被匹配成功,则 j == t.lengthreturn i - j  # 此时 s 串的 i - j 位置就是首次匹配 t 的子串起始位置else:return -1

    JavaScript算法源码

    暴力解法
    /*** @param {string} haystack* @param {string} needle* @return {number}*/
    var strStr = function (s, t) {for (let k = 0; k <= s.length - t.length; k++) {let i = k;let j = 0;while (j < t.length && s[i] == t[j]) {i++;j++;}if (j == t.length) {return k;}}return -1;
    };
    KMP算法
    /*** @param {string} haystack* @param {string} needle* @return {number}*/
    var strStr = function (s, t) {const next = getNext(t); // 生成 t 串的前缀表let i = 0;let j = 0;while (i < s.length && j < t.length) {if (s[i] == t[j]) {i++;j++;} else if (j > 0) {j = next[j - 1]; // 若 s[i] != t[j] && j > 0,则 i 指针不动,j 指针回退到 t 串的 next[j-1] 位置} else {i++; // 若 s[i] != t[j] && j == 0,则表示 s[i] 和 t[0] 首个字符就匹配失败,我们只能进入下一轮,即 i++}}if (j == t.length) { // 若 t 串所有字符都被匹配成功,则 j == t.lengthreturn i - j; // 此时 s 串的 i - j 位置就是首次匹配 t 的子串起始位置} else {return -1;}
    };var getNext = function (t) {const next = new Array(t.length).fill(0);let j = 1;let k = next[j - 1];while (j < t.length) {if (t[j] == t[k]) {next[j] = k + 1; // 前缀范围 t[0,k] == 后缀范围 t[j-k,j]  此时最长相同前后缀长度为:k+1j++;k++;} else if (k > 0) {k = next[k - 1]; // 若 t[j] != t[k] && k > 0,则缩短前缀部分,k = next[k-1] 后继续比较 t[k] 和 t[j]} else {j++; // 若 t[j] != t[k] && k == 0,则 k 无法继续后退,此时可以认为 next[j] 为 0,进行j++,继续求解下一个next[j]}}return next;
    }

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/897195.shtml

    如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

    相关文章

    Spring Boot 异步编程

    文章目录 一、异步方法的使用1. 开启异步支持2. 定义异步方法3. 调用异步方法踩坑记录心得体会 二、线程池配置1. 自定义线程池2. 使用自定义线程池踩坑记录心得体会 三、异步任务的监控与管理1. 日志记录2. 异常处理3. 线程池监控踩坑记录心得体会 在现代应用程序开发中&#…

    0.大模型开发知识点需求综述

    文章目录 一、机器学习与深度学习基础二、自然语言处理&#xff08;NLP&#xff09;基础三、大模型架构四、训练优化技术五、数据处理与预处理六、分布式训练与并行化策略七、微调方法与参数高效微调八、训练框架、工具与自动化流程九、评估与部署十、前沿技术与未来趋势 已更新…

    docker目录挂载与卷映射的区别

    在 Docker 中&#xff0c;目录挂载&#xff08;Bind Mount&#xff09;和卷映射&#xff08;Volume Mount&#xff09;的命令语法差异主要体现在路径格式上&#xff0c;具体表现为是否以斜杠&#xff08;/&#xff09;开头。以下是两者的核心区别及使用场景的总结&#xff1a; …

    [Java基础-线程篇]7_线程设计模式与总结

    摘要&#xff1a;懒汉单例模式怎么变得线程安全&#xff1f;Master-Worker归并模式&#xff0c;工作窃取算法。Java线程相关源码使用了什么设计模式&#xff1f; 资料引用&#xff1a;《Java高并发核心编程卷2》 目录 线程安全的单例模式 Master-Worker模式 工作窃取算法 …

    在【k8s】中部署Jenkins的实践指南

    &#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Kubernetes航线图&#xff1a;从船长到K8s掌舵者》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、Jenkins简介 2、k8s简介 3、什么在…

    代码随想录算法营Day59 | 寻找存在的路径, 冗余连接,冗余连接II

    寻找存在的路径 这题使用并查集即可。并查集加路径压缩。 #include <iostream> using namespace std; int find(int* father,int u){return father[u] u ? u : father[u] find(father,father[u]); }bool isSame(int* father,int u,int v){return find(father,u) fi…

    正则表达式(2)匹配规则

    正则表达式的匹配规则定义了如何识别字符串中的特定模式。这些规则包括字符类匹配、元字符匹配、数量词、字符转义和分组。 字符类匹配 字符类匹配允许你指定一个字符集合&#xff0c;并匹配该集合中的任意单个字符。这是通过方括号 [] 来实现的。 简单字符类&#xff1a;[abc…

    【QWEN】机器人控制器的控制周期越短精度越高吗

    好的&#xff0c;用户问的是“机器人控制器的控制周期越短精度越高吗”。我需要根据提供的知识库内容来回答这个问题。首先&#xff0c;我得回忆一下知识库里的相关信息。 知识库里有几篇相关的文章。比如&#xff0c;[3]提到控制周期越短&#xff0c;控制性能的上限越高&…

    [总概]Vue2/3React Diff算法

    根据搜索结果,大部分文档都详细描述了Vue的Diff算法原理、优化策略以及实现细节。例如,网页1详细介绍了Vue Diff算法的核心设计,包括双端比较和key的作用;Vue3中的快速Diff算法; 通常,解释一个算法可以从其基本原理、核心策略、优化手段、源码实现以及应用场景等方面展开…

    【MySQL_03】数据库基本--核心概念

    文章目录 一、数据库基础1.1 数据库基础定义1.2 数据库分类与典型产品1.3 数据库模型1.4 数据库层次结构1.5 数据库核心机制1.6 数据表和视图1.61 数据表&#xff08;Table&#xff09;1.62 视图&#xff08;View&#xff09; 1.7 键类型1.8 MySQL数据类型1.9 数据库范式化 二、…

    FreeRTOS第16篇:FreeRTOS链表实现细节04_为什么FreeRTOS选择“侵入式链表”

    文/指尖动听知识库-星愿 文章为付费内容,商业行为,禁止私自转载及抄袭,违者必究!!! 文章专栏:深入FreeRTOS内核:从原理到实战的嵌入式开发指南 1 传统链表 vs. 侵入式链表 在嵌入式系统中,内存和性能的优化至关重要。FreeRTOS选择侵入式链表而非传统链表,其背后是内…

    STM32读写片内FLASH 笔记

    文章目录 前言STM32F105的内部ROM分布STM32F10x的闪存擦写解锁FPECMain FLASH 的编写 main Flash的擦除注意点 前言 在通过OTA的方式对设备进行升级&#xff0c;若在使用内部FLASH装载固件程序的方式下&#xff0c;需要擦写 内部FLASH 从而实现把新的固件程序写入到 内部FLASH…

    Python爬虫实战:爬取财金网实时财经信息

    注意:以下内容仅供技术研究,请遵守目标网站的robots.txt规定,控制请求频率避免对目标服务器造成过大压力! 一、引言 在当今数字化时代,互联网数据呈爆炸式增长,其中蕴含着巨大的商业价值、研究价值和社会价值。从金融市场动态分析到行业趋势研究,从舆情监测到学术信息收…

    3.3.2 用仿真图实现点灯效果

    文章目录 文章介绍Keil生成.hex代码Proteus仿真图中导入.hex代码文件开始仿真 文章介绍 点灯之前需要准备好仿真图keil代码 仿真图参考前文&#xff1a;3.3.2 Proteus第一个仿真图 keil安装参考前文&#xff1a;3.1.2 Keil4安装教程 keil新建第一个项目参考前文&#xff1a;3.1…

    996引擎-问题处理:实现自定义道具变身卡

    996引擎-问题处理:实现自定义道具变身卡 方案一、修改角色外观(武器、衣服、特效) 实现变身先看效果创建个NPC测试效果方案二、利用 Buff 实现变身创建:变身Buff配buff表,实现人物变形测试NPC创建道具:变身卡配item表,添加道具:变身卡触发函数参考资料方案一、修改角色外…

    AI视频领域的DeepSeek—阿里万相2.1图生视频

    让我们一同深入探索万相 2.1 &#xff0c;本文不仅介绍其文生图和文生视频的使用秘籍&#xff0c;还将手把手教你如何利用它实现图生视频。 如下为生成的视频效果&#xff08;我录制的GIF动图&#xff09; 如下为输入的图片 目录 1.阿里巴巴全面开源旗下视频生成模型万相2.1模…

    驱动 AI 边缘计算新时代!高性能 i.MX 95 应用平台引领未来

    智慧浪潮崛起&#xff1a;AI与边缘计算的时代 正悄然深植于我们的日常生活之中&#xff0c;无论是火热的 ChatGPT 与 DeepSeek 语言模型&#xff0c;亦或是 Meta 智能眼镜&#xff0c;AI 技术已经无形地影响着我们的生活。这股变革浪潮并未停歇&#xff0c;而是进一步催生了更高…

    如何快速判断IP是否为代理

    1.探究IP地址的地理分布 代理IP的所在位置&#xff0c;往往与用户实际所在地不吻合。可以通过运用WHOIS查询工具或在线IP地址定位服务&#xff0c;输入所需查询的IP&#xff0c;即可获得其地理位置信息。 若该信息显示的位置并非用户所在城市或显示为知名代理服务器节点&…

    从CL1看生物计算机的创新突破与发展前景:技术、应用与挑战的多维度剖析

    一、引言 1.1 研究背景与意义 随着科技的飞速发展&#xff0c;计算机技术已经成为推动现代社会进步的核心力量之一。从最初的电子管计算机到如今的大规模集成电路计算机&#xff0c;计算机的性能得到了极大的提升&#xff0c;应用领域也不断拓展。然而&#xff0c;传统计算机…

    AI革命先锋:DeepSeek与蓝耘通义万相2.1的无缝融合引领行业智能化变革

    云边有个稻草人-CSDN博客 目录 引言 一、什么是DeepSeek&#xff1f; 1.1 DeepSeek平台概述 1.2 DeepSeek的核心功能与技术 二、蓝耘通义万相2.1概述 2.1 蓝耘科技简介 2.2 蓝耘通义万相2.1的功能与优势 1. 全链条智能化解决方案 2. 强大的数据处理能力 3. 高效的模型…