java中基本数据类型结构 " />
最长公共字符串是指在多个字符串中找到一个最长的字符串,使得它在每个字符串中都出现过。这个问题是非常经典且重要的问题,也是计算机科学中的一个典型问题,对于各种应用来说非常有用。
Java中的基本数据类型结构包括整型、浮点型、字符型、布尔型等,这些数据类型在解决算法问题中也是必不可少的。下面我们就来讲讲在Java中实现最长公共字符串的算法和基本数据类型的应用。
一、暴力法
最简单直接的算法是暴力枚举,即逐一比较每一对字符串中相同位置的字符是否相同,然后尝试匹配所有可能的子串找出最长的公共子串。
算法流程:
首先,我们需要找到所有字符串的最短字符串,并先假设它就是答案。
然后,我们逐一枚举最短字符串的所有子串,检查该子串是否在其他字符串中都存在。
如果该子串在其他字符串中都存在,则将它标记为公共子串。
对于所有标记为公共子串的子串,找到其中最长的一个子串就是最长公共子串。
该算法的时间复杂度为$O(mn^2)$,其中$m$为所有字符串的长度之和,$n$为字符串的数目。这个算法在处理海量数据时比较困难,但对于小规模数据和初学者来说十分友好,尤其是对于一些特殊情况下能够达到最优解。
Java代码:
```java
public static String longestCommonSubstring(String[] strs) {
int n = strs.length;
if (n == 0) return "";
String shortest = strs[0];
for (String str : strs) {
if (str.length() < shortest.length()) shortest = str;
}
int len = shortest.length();
for (int i = len; i > 0; i--) {
for (int j = 0; j <= len - i; j++) {
String substr = shortest.substring(j, j + i);
boolean flag = true;
for (int k = 0; k < n; k++) {
if (!strs[k].contains(substr)) {
flag = false;
break;
}
}
if (flag) return substr;
}
}
return "";
}
```
二、动态规划法
动态规划法是一种十分高效的算法,在处理最长公共子序列问题时也有很好的应用。这里我们可以使用两个在本题中非常关键的数据结构:标志数组$dp$和最长公共子串数组$len$。
其中,$dp[i][j]$表示当前考虑到的两个字符串中以第一个字符串第$i$个字符和第二个字符串第$j$个字符结尾的最长公共子串长度,最终的结果即为$max(len)$。
具体算法流程如下:
设第一个字符串的长度为$m$,第二个字符串的长度为$n$。
令数组$dp$全部初始化为0。
依次枚举第一个字符串中的每个字符$i$以及第二个字符串中的每个字符$j$。
若第一个字符串第$i$个字符等于第二个字符串第$j$个字符,则令$dp[i][j]=dp[i-1][j-1]+1$。
如果$dp[i][j]$大于最长公共子串的长度,则更新最长公共子串的长度。
返回$max(len)$即可。
该算法的时间复杂度为$O(mn)$。虽然可能需要比暴力枚举法更多的空间,但却是处理海量数据时的首选算法。
Java代码:
```java
public static String longestCommonSubstring(String[] strs) {
int n = strs.length;
if (n == 0) return "";
int m = strs[0].length();
int[][] dp = new int[m + 1][n + 1];
int len = 0, end = -1;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (strs[j - 1].charAt(i - 1) == strs[0].charAt(i - 1)) {
dp[i][j] = dp[i - 1][j - 1] + 1;
if (dp[i][j] > len) {
len = dp[i][j];
end = i;
}
}
}
}
return len == 0 ? "" : strs[0].substring(end - len, end);
}
```
三、后缀数组法
后缀数组是一种非常优秀的数据结构,它可以在$O(n\log n)$的时间复杂度内构建出一个字符串的后缀数组,并能够在$O(m\log n)$的时间内完成最长公共子串的查找任务。
它的基本思想是将字符串的所有后缀按字典序排序,并记录它们的起始位置。这样一个字符串的最长公共子串,就是各个字符串后缀之间的最长公共前缀。
具体算法流程如下:
首先,生成原字符串的所有后缀,并构建出这些后缀的后缀数组。
然后,遍历每一个相邻的后缀,计算它们之间的最长公共前缀。
如果该最长公共前缀的长度大于当前结果的长度,则更新最长公共子串的长度和其起始位置。
返回最终的最长公共子串。
该算法的时间复杂度为$O(n\log^2 n)$,在海量数据的处理中效率非常高。
Java代码:
```java
public static String longestCommonSubstring(String[] strs) {
int n = strs.length;
if (n == 0) return "";
if (n == 1) return strs[0];
for (int i = 0; i < n; i++) {
if (strs[i].length() == 0) return "";
}
SuffixArray sa = new SuffixArray();
sa.build(strs[0], strs);
String ans = "";
int ansLen = 0;
for (int i = 1; i <= strs[0].length(); i++) {
for (int j = i; j <= strs[0].length(); j++) {
int cnt = 1;
boolean flag = true;
for (int k = 1; k < n; k++) {
int len = sa.lcp(i, j, sa.height[k]);
if (len == 0) {
flag = false;
break;
} else {
cnt++;
}
}
if (flag && cnt == n && ansLen < j - i) {
ans = strs[0].substring(sa.sa[i] - 1, sa.sa[i] - 1 + j - i);
ansLen = j - i;
}
}
}
return ans;
}
class SuffixArray {
int[] sa;
int[] height;
void build(String s, String[] t) {
int n = s.length(), m = t.length;
int[] x = new int[n + m], y = new int[n + m];
int[] c = new int[m + 1];
sa = new int[n + 1];
height = new int[n + 1];
for (int i = 0; i < n; i++) {
x[i] = s.charAt(i);
}
for (int i = 0; i < m; i++) {
x[n + i] = (int) t[i].charAt(0);
}
for (int i = 0; i < n + m; i++) {
c[x[i]]++;
}
for (int i = 1; i <= m; i++) {
c[i] += c[i - 1];
}
for (int i = n + m - 1; i >= 0; i--) {
c[x[i]]--;
sa[c[x[i]]] = i;
}
for (int k = 1; k <= n; k *= 2) {
int p = 0;
for (int i = n; i > n - k; i--) {
y[p++] = i;
}
for (int i = 0; i < n; i++) {
if (sa[i] >= k) y[p++] = sa[i] - k;
}
for (int i = n + m - 1; i >= 0; i--) {
c[x[y[i]]]--;
sa[c[x[y[i]]]] = y[i];
}
int[] t1 = x;
x = y;
y = t1;
p = 1;
x[sa[0]] = 0;
for (int i = 1; i < n; i++) {
if (y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k]) {
x[sa[i]] = p - 1;
} else {
x[sa[i]] = p++;
}
}
if (p >= n) break;
m = p;
}
int k = 0;
for (int i = 0; i < n; i++) {
if (k > 0) k--;
if (x[i] == 0) continue;
int j = sa[x[i] - 1];
while (i + k < n && j + k < n && s.charAt(i + k) == s.charAt(j + k)) k++;
height[x[i]] = k;
}
}
int lcp(int i, int j, int k) {
if (i == j) return height[i];
if (i > j) {
int tmp = i;
i = j;
j = tmp;
}
int ans = height[i + 1];
for (int p = i + 2; p <= j; p++) {
if (height[p] < ans) ans = height[p];
}
if (k < ans) ans = k;
return ans;
}
}
```
最后,以上三种算法都能够在Java中十分方便地使用Java的基本数据类型来实现,包括整型、浮点类型、字符类型等。在实际应用中,我们可以根据具体的问题来选择不同的算法和数据类型,得到最优的运行效果。
壹涵网络我们是一家专注于网站建设、企业营销、网站关键词排名、AI内容生成、新媒体营销和短视频营销等业务的公司。我们拥有一支优秀的团队,专门致力于为客户提供优质的服务。
我们致力于为客户提供一站式的互联网营销服务,帮助客户在激烈的市场竞争中获得更大的优势和发展机会!
发表评论 取消回复