串是数据结构中一种重要的数据类型,广泛应用于文本处理、信息检索等领域。本文将从串的基本概念、存储实现、应用举例以及总结核心知识点四个方面进行详细讲解,帮助大家更好地理解和掌握串这一数据结构。
一、串的基本概念及其抽象数据类型
1.1 串的定义
串(String)是由零个或多个字符组成的有限序列,又名字符串。一般记为:S=′a1a2...an′(n≥0)。其中,S是串名,单引号括起来的字符序列是串的值;an可以是字母、数字或其他字符;串中字符的个数n称为串的长度。当n=0时,称为空串。例如,"Hello, World!"
是一个长度为13的串。
1.2 串的重要术语
-
子串与主串:串中任意个连续的字符组成的子序列称为该串的子串,相应地,包含子串的串称为主串。
-
空串与空格串:长度为零的字符串称为空串,由一个或多个空格组成的串称为空格串。
-
串相等:当且仅当两个串的值相等时,称这两个串是相等的。
1.3 串的抽象数据类型
串的抽象数据类型定义如下:
-
数据对象:D={ai∣ai∈CharacterSet,i=1,2,…,n;n≥0}
-
数据关系:R={<ai−1,ai>∣ai−1,ai∈D,i=2,…,n;n≥0}
-
串的ADT包括以下基本操作:
-
获取长度:返回串的长度。
-
获取字符:返回指定位置的字符。
-
插入字符:在指定位置插入一个字符。
-
删除字符:删除指定位置的字符。
-
查找子串:在串中查找子串的位置。
-
替换子串:将串中的子串替换为另一个子串。
ADT String {
数据对象:D = {a_i | a_i ∈ CharacterSet, i=1,2,...,n}
数据关系:R = {<a_i,a_i+1> | a_i,a_i+1 ∈ D}
基本操作:
StrAssign(&T, chars) # 字符串赋值
StrCompare(S, T) # 比较操作
StrLength(S) # 求长度
Concat(&T, S1, S2) # 连接操作
SubString(&Sub, S, pos, len) # 求子串
Index(S, T, pos) # 模式匹配
Replace(&S, T, V) # 替换操作
StrInsert(&S, pos, T) # 插入操作
StrDelete(&S, pos, len) # 删除操作
}
二、串的存储实现
2.1 定长顺序串
定长顺序串是将串设计成一种静态结构类型,串的存储分配是在编译时完成的。其结构体定义如下:
#define MAXLEN 255
typedef struct{
char ch[MAXLEN]; //每个分量存储一个字符
int length; //串的实际长度
}SString;
基本操作实现:
-
初始化:将串的长度置为0,所有字符位置初始化为空字符。
-
赋值:将一个字符串常量赋值给串,同时更新串的长度。
-
求子串:根据给定的位置和长度,从主串中取出相应的子串。
-
连接:将两个串连接起来,形成一个新的串。如果连接后的长度超过预定义的最大长度,则进行截断。
-
插入:在指定位置插入一个子串。如果插入后长度超过最大长度,则进行截断。
-
删除:从指定位置删除一定长度的子串。
C语言实现
#define MAX_LEN 100 // 定义最大长度为100
// 定义串的结构体
typedef struct {
char data[MAX_LEN]; // 使用数组存储字符
int length; // 当前串的长度
} String;
// 初始化串
void initString(String *s) {
s->length = 0; // 初始长度为0
}
// 在指定位置插入字符
void insertChar(String *s, int pos, char c) {
if (pos >= 0 && pos <= s->length && s->length < MAX_LEN) { // 检查位置是否合法且未超出容量
for (int i = s->length; i > pos; i--) { // 从后往前移动字符
s->data[i] = s->data[i-1];
}
s->data[pos] = c; // 插入新字符
s->length++; // 长度加1
}
}
// 删除指定位置的字符
void deleteChar(String *s, int pos) {
if (pos >= 0 && pos < s->length) { // 检查位置是否合法
for (int i = pos; i < s->length - 1; i++) { // 从前往后移动字符
s->data[i] = s->data[i+1];
}
s->length--; // 长度减1
}
}
// 查找子串
int findSubstring(String *s, const char *sub) {
int subLen = strlen(sub); // 获取子串长度
for (int i = 0; i <= s->length - subLen; i++) { // 遍历串
int j;
for (j = 0; j < subLen; j++) { // 比较子串
if (s->data[i + j] != sub[j]) {
break; // 如果不匹配,跳出循环
}
}
if (j == subLen) { // 如果完全匹配
return i; // 返回子串起始位置
}
}
return -1; // 未找到返回-1
}
// 替换子串
void replaceSubstring(String *s, const char *oldSub, const char *newSub) {
int pos = findSubstring(s, oldSub); // 查找旧子串位置
if (pos != -1) {
int oldLen = strlen(oldSub); // 旧子串长度
int newLen = strlen(newSub); // 新子串长度
if (newLen > oldLen) { // 如果新子串更长
if (s->length + newLen - oldLen > MAX_LEN) { // 检查是否超出容量
printf("Replacement exceeds maximum length.\n");
return;
}
for (int i = s->length - 1; i >= pos + oldLen; i--) { // 移动字符为新子串腾位置
s->data[i + newLen - oldLen] = s->data[i];
}
} else if (newLen < oldLen) { // 如果新子串更短
for (int i = pos + oldLen - 1; i >= pos + newLen; i--) { // 移动字符填补空缺
s->data[i] = s->data[i + oldLen - newLen];
}
}
for (int i = 0; i < newLen; i++) { // 插入新子串
s->data[pos + i] = newSub[i];
}
s->length += newLen - oldLen; // 更新长度
}
}
C++实现
#include <iostream>
#include <cstring>
using namespace std;
class String {
private:
char *data; // 指针指向动态分配的内存
int length; // 当前长度
int capacity; // 容量
public:
String() : length(0), capacity(100) { // 构造函数
data = new char[capacity]; // 动态分配内存
}
// 在指定位置插入字符
void insertChar(int pos, char c) {
if (pos >= 0 && pos <= length) { // 检查位置是否合法
if (length == capacity) { // 如果当前长度等于容量
capacity *= 2; // 扩容
char *newData = new char[capacity]; // 新分配内存
for (int i = 0; i < length; i++) { // 复制原有数据
newData[i] = data[i];
}
delete[] data; // 释放旧内存
data = newData; // 指向新内存
}
for (int i = length; i > pos; i--) { // 从后往前移动字符
data[i] = data[i-1];
}
data[pos] = c; // 插入新字符
length++; // 长度加1
}
}
// 删除指定位置的字符
void deleteChar(int pos) {
if (pos >= 0 && pos < length) { // 检查位置是否合法
for (int i = pos; i < length - 1; i++) { // 从前往后移动字符
data[i] = data[i+1];
}
length--; // 长度减1
}
}
// 查找子串
int findSubstring(const char *sub) {
int subLen = strlen(sub); // 获取子串长度
for (int i = 0; i <= length - subLen; i++) { // 遍历串
int j;
for (j = 0; j < subLen; j++) { // 比较子串
if (data[i + j] != sub[j]) {
break; // 如果不匹配,跳出循环
}
}
if (j == subLen) { // 如果完全匹配
return i; // 返回子串起始位置
}
}
return -1; // 未找到返回-1
}
// 替换子串
void replaceSubstring(const char *oldSub, const char *newSub) {
int pos = findSubstring(oldSub); // 查找旧子串位置
if (pos != -1) {
int oldLen = strlen(oldSub); // 旧子串长度
int newLen = strlen(newSub); // 新子串长度
if (newLen > oldLen) { // 如果新子串更长
if (length + newLen - oldLen > capacity) { // 检查是否超出容量
capacity = length + newLen - oldLen + 100; // 扩容
char *newData = new char[capacity]; // 新分配内存
for (int i = 0; i < length; i++) { // 复制原有数据
newData[i] = data[i];
}
delete[] data; // 释放旧内存
data = newData; // 指向新内存
}
for (int i = length - 1; i >= pos + oldLen; i--) { // 移动字符为新子串腾位置
data[i + newLen - oldLen] = data[i];
}
} else if (newLen < oldLen) { // 如果新子串更短
for (int i = pos + oldLen - 1; i >= pos + newLen; i--) { // 移动字符填补空缺
data[i] = data[i + oldLen - newLen];
}
}
for (int i = 0; i < newLen; i++) { // 插入新子串
data[pos + i] = newSub[i];
}
length += newLen - oldLen; // 更新长度
}
}
// 析构函数,释放内存
~String() {
delete[] data;
}
};
Java实现
public class String {
private char[] data; // 使用字符数组存储串
private int length; // 当前长度
// 构造函数
public String() {
data = new char[100]; // 初始容量为100
length = 0; // 初始长度为0
}
// 在指定位置插入字符
public void insertChar(int pos, char c) {
if (pos >= 0 && pos <= length) { // 检查位置是否合法
if (length == data.length) { // 如果当前长度等于容量
char[] newData = new char[data.length * 2]; // 扩容
System.arraycopy(data, 0, newData, 0, data.length); // 复制原有数据
data = newData; // 指向新数组
}
for (int i = length; i > pos; i--) { // 从后往前移动字符
data[i] = data[i-1];
}
data[pos] = c; // 插入新字符
length++; // 长度加1
}
}
// 删除指定位置的字符
public void deleteChar(int pos) {
if (pos >= 0 && pos < length) { // 检查位置是否合法
for (int i = pos; i < length - 1; i++) { // 从前往后移动字符
data[i] = data[i+1];
}
length--; // 长度减1
}
}
// 查找子串
public int findSubstring(String sub) {
int subLen = sub.length(); // 获取子串长度
for (int i = 0; i <= length - subLen; i++) { // 遍历串
int j;
for (j = 0; j < subLen; j++) { // 比较子串
if (data[i + j] != sub.charAt(j)) {
break; // 如果不匹配,跳出循环
}
}
if (j == subLen) { // 如果完全匹配
return i; // 返回子串起始位置
}
}
return -1; // 未找到返回-1
}
// 替换子串
public void replaceSubstring(String oldSub, String newSub) {
int pos = findSubstring(oldSub); // 查找旧子串位置
if (pos != -1) {
int oldLen = oldSub.length(); // 旧子串长度
int newLen = newSub.length(); // 新子串长度
if (newLen > oldLen) { // 如果新子串更长
if (length + newLen - oldLen > data.length) { // 检查是否超出容量
char[] newData = new char[data.length + newLen - oldLen + 100]; // 扩容
System.arraycopy(data, 0, newData, 0, data.length); // 复制原有数据
data = newData; // 指向新数组
}
for (int i = length - 1; i >= pos + oldLen; i--) { // 移动字符为新子串腾位置
data[i + newLen - oldLen] = data[i];
}
} else if (newLen < oldLen) { // 如果新子串更短
for (int i = pos + oldLen - 1; i >= pos + newLen; i--) { // 移动字符填补空缺
data[i] = data[i + oldLen - newLen];
}
}
for (int i = 0; i < newLen; i++) { // 插入新子串
data[pos + i] = newSub.charAt(i);
}
length += newLen - oldLen; // 更新长度
}
}
// 转换为字符串表示
@Override
public String toString() {
return new String(data, 0, length);
}
}
Python实现
class String:
def __init__(self):
self.data = [] # 使用列表存储字符
self.length = 0 # 当前长度
# 在指定位置插入字符
def insert_char(self, pos, c):
if 0 <= pos <= self.length: # 检查位置是否合法
self.data.insert(pos, c) # 使用列表的insert方法插入字符
self.length += 1 # 长度加1
# 删除指定位置的字符
def delete_char(self, pos):
if 0 <= pos < self.length: # 检查位置是否合法
del self.data[pos] # 使用del删除字符
self.length -= 1 # 长度减1
# 查找子串
def find_substring(self, sub):
return ''.join(self.data).find(sub) # 将列表转换为字符串后查找子串
# 替换子串
def replace_substring(self, old_sub, new_sub):
pos = self.find_substring(old_sub) # 查找旧子串位置
if pos != -1:
self.data = self.data[:pos] + list(new_sub) + self.data[pos + len(old_sub):] # 替换子串
self.length = len(self.data) # 更新长度
# 转换为字符串表示
def __str__(self):
return ''.join(self.data)
2.2 堆串
堆串的存储空间是在程序执行过程中动态分配的。其结构体定义如下:
typedef struct{
char *ch; //按串长分配存储区,ch指向串的基地址
int length; //串的长度
}HString;
基本操作实现:
-
初始化:将ch指针置为NULL,长度置为0。
-
赋值:先释放原有的存储空间,然后根据字符串常量的长度动态分配存储空间,并将字符串复制到分配的空间中。
-
求子串:动态分配存储空间来存储子串。
-
连接:动态分配足够大的存储空间来存储连接后的串。
-
插入:动态分配新的存储空间,将原串和插入的子串拼接起来。
-
删除:通过调整指针和长度来实现删除操作,不需要移动字符。
C语言实现
#include <stdlib.h>
#include <string.h>
// 定义串的结构体
typedef struct {
char *data; // 指针指向动态分配的内存
int length; // 当前长度
int capacity; // 容量
} String;
// 初始化串
void initString(String *s, int capacity) {
s->data = (char*)malloc(capacity * sizeof(char)); // 动态分配内存
s->length = 0; // 初始长度为0
s->capacity = capacity; // 设置初始容量
}
// 在指定位置插入字符
void insertChar(String *s, int pos, char c) {
if (pos >= 0 && pos <= s->length) { // 检查位置是否合法
if (s->length == s->capacity) { // 如果当前长度等于容量
s->capacity *= 2; // 扩容
s->data = (char*)realloc(s->data, s->capacity * sizeof(char)); // 重新分配内存
}
for (int i = s->length; i > pos; i--) { // 从后往前移动字符
s->data[i] = s->data[i-1];
}
s->data[pos] = c; // 插入新字符
s->length++; // 长度加1
}
}
// 删除指定位置的字符
void deleteChar(String *s, int pos) {
if (pos >= 0 && pos < s->length) { // 检查位置是否合法
for (int i = pos; i < s->length - 1; i++) { // 从前往后移动字符
s->data[i] = s->data[i+1];
}
s->length--; // 长度减1
}
}
// 查找子串
int findSubstring(String *s, const char *sub) {
int subLen = strlen(sub); // 获取子串长度
for (int i = 0; i <= s->length - subLen; i++) { // 遍历串
int j;
for (j = 0; j < subLen; j++) { // 比较子串
if (s->data[i + j] != sub[j]) {
break; // 如果不匹配,跳出循环
}
}
if (j == subLen) { // 如果完全匹配
return i; // 返回子串起始位置
}
}
return -1; // 未找到返回-1
}
// 替换子串
void replaceSubstring(String *s, const char *oldSub, const char *newSub) {
int pos = findSubstring(s, oldSub); // 查找旧子串位置
if (pos != -1) {
int oldLen = strlen(oldSub); // 旧子串长度
int newLen = strlen(newSub); // 新子串长度
if (newLen > oldLen) { // 如果新子串更长
if (s->length + newLen - oldLen > s->capacity) { // 检查是否超出容量
s->capacity = s->length + newLen - oldLen; // 扩容
s->data = (char*)realloc(s->data, s->capacity * sizeof(char)); // 重新分配内存
}
for (int i = s->length - 1; i >= pos + oldLen; i--) { // 移动字符为新子串腾位置
s->data[i + newLen - oldLen] = s->data[i];
}
} else if (newLen < oldLen) { // 如果新子串更短
for (int i = pos + oldLen - 1; i >= pos + newLen; i--) { // 移动字符填补空缺
s->data[i] = s->data[i + oldLen - newLen];
}
}
for (int i = 0; i < newLen; i++) { // 插入新子串
s->data[pos + i] = newSub[i];
}
s->length += newLen - oldLen; // 更新长度
}
}
// 释放内存
void freeString(String *s) {
free(s->data); // 释放动态分配的内存
}
C++实现
#include <iostream>
#include <cstring>
using namespace std;
// 定义节点结构
struct Node {
char data; // 存储字符
Node *next; // 指向下一个节点
};
// 定义串的类
class String {
private:
Node *head; // 指向头节点
int length; // 当前长度
public:
// 构造函数
String() : head(NULL), length(0) {}
// 在指定位置插入字符
void insertChar(int pos, char c) {
if (pos >= 0 && pos <= length) { // 检查位置是否合法
Node *newNode = new Node(); // 创建新节点
newNode->data = c; // 设置节点数据
if (pos == 0) { // 如果插入到头部
newNode->next = head;
head = newNode;
} else { // 插入到指定位置
Node *current = head;
for (int i = 0; i < pos - 1; i++) { // 遍历到前一个节点
current = current->next;
}
newNode->next = current->next;
current->next = newNode;
}
length++; // 长度加1
}
}
// 删除指定位置的字符
void deleteChar(int pos) {
if (pos >= 0 && pos < length) { // 检查位置是否合法
if (pos == 0) { // 如果删除头部
Node *temp = head;
head = head->next;
delete temp; // 释放内存
} else { // 删除指定位置
Node *current = head;
for (int i = 0; i < pos - 1; i++) { // 遍历到前一个节点
current = current->next;
}
Node *temp = current->next;
current->next = temp->next;
delete temp; // 释放内存
}
length--; // 长度减1
}
}
// 查找子串
int findSubstring(const char *sub) {
Node *current = head;
StringBuilder sb; // 构建当前串
while (current != NULL) {
sb.append(current->data);
current = current->next;
}
return sb.toString().find(sub); // 查找子串
}
// 替换子串
void replaceSubstring(const char *oldSub, const char *newSub) {
int pos = findSubstring(oldSub); // 查找旧子串位置
if (pos != -1) {
deleteSubstring(pos, strlen(oldSub)); // 删除旧子串
for (int i = strlen(newSub) - 1; i >= 0; i--) { // 插入新子串
insertChar(pos, newSub[i]);
}
}
}
// 删除子串
void deleteSubstring(int pos, int len) {
for (int i = 0; i < len; i++) {
deleteChar(pos); // 逐个删除字符
}
}
// 转换为字符串表示
string toString() {
Node *current = head;
string result;
while (current != NULL) {
result += current->data;
current = current->next;
}
return result;
}
// 析构函数,释放内存
~String() {
while (head != NULL) {
Node *temp = head;
head = head->next;
delete temp;
}
}
};
Java实现
public class String {
private class Node {
char data; // 存储字符
Node next; // 指向下一个节点
}
private Node head; // 指向头节点
private int length; // 当前长度
// 构造函数
public String() {
head = null; // 初始头节点为null
length = 0; // 初始长度为0
}
// 在指定位置插入字符
public void insertChar(int pos, char c) {
if (pos >= 0 && pos <= length) { // 检查位置是否合法
Node newNode = new Node(); // 创建新节点
newNode.data = c; // 设置节点数据
if (pos == 0) { // 如果插入到头部
newNode.next = head;
head = newNode;
} else { // 插入到指定位置
Node current = head;
for (int i = 0; i < pos - 1; i++) { // 遍历到前一个节点
current = current.next;
}
newNode.next = current.next;
current.next = newNode;
}
length++; // 长度加1
}
}
// 删除指定位置的字符
public void deleteChar(int pos) {
if (pos >= 0 && pos < length) { // 检查位置是否合法
if (pos == 0) { // 如果删除头部
head = head.next;
} else { // 删除指定位置
Node current = head;
for (int i = 0; i < pos - 1; i++) { // 遍历到前一个节点
current = current.next;
}
current.next = current.next.next;
}
length--; // 长度减1
}
}
// 查找子串
public int findSubstring(String sub) {
Node current = head;
StringBuilder sb = new StringBuilder(); // 构建当前串
while (current != null) {
sb.append(current.data);
current = current.next;
}
return sb.toString().indexOf(sub); // 查找子串
}
// 替换子串
public void replaceSubstring(String oldSub, String newSub) {
int pos = findSubstring(oldSub); // 查找旧子串位置
if (pos != -1) {
deleteSubstring(pos, oldSub.length()); // 删除旧子串
for (int i = newSub.length() - 1; i >= 0; i--) { // 插入新子串
insertChar(pos, newSub.charAt(i));
}
}
}
// 删除子串
private void deleteSubstring(int pos, int len) {
for (int i = 0; i < len; i++) {
deleteChar(pos); // 逐个删除字符
}
}
// 转换为字符串表示
@Override
public String toString() {
Node current = head;
StringBuilder sb = new StringBuilder();
while (current != null) {
sb.append(current.data);
current = current.next;
}
return sb.toString();
}
}
Python实现
class Node:
def __init__(self, data):
self.data = data # 存储字符
self.next = None # 指向下一个节点
class String:
def __init__(self):
self.head = None # 指向头节点
self.length = 0 # 当前长度
# 在指定位置插入字符
def insert_char(self, pos, c):
if 0 <= pos <= self.length: # 检查位置是否合法
new_node = Node(c) # 创建新节点
if pos == 0: # 如果插入到头部
new_node.next = self.head
self.head = new_node
else: # 插入到指定位置
current = self.head
for _ in range(pos - 1): # 遍历到前一个节点
current = current.next
new_node.next = current.next
current.next = new_node
self.length += 1 # 长度加1
# 删除指定位置的字符
def delete_char(self, pos):
if 0 <= pos < self.length: # 检查位置是否合法
if pos == 0: # 如果删除头部
self.head = self.head.next
else: # 删除指定位置
current = self.head
for _ in range(pos - 1): # 遍历到前一个节点
current = current.next
current.next = current.next.next
self.length -= 1 # 长度减1
# 查找子串
def find_substring(self, sub):
current = self.head
sb = [] # 构建当前串
while current is not None:
sb.append(current.data)
current = current.next
return ''.join(sb).find(sub) # 查找子串
# 替换子串
def replace_substring(self, old_sub, new_sub):
pos = self.find_substring(old_sub) # 查找旧子串位置
if pos != -1:
self.delete_substring(pos, len(old_sub)) # 删除旧子串
for i in reversed(new_sub): # 插入新子串
self.insert_char(pos, i)
# 删除子串
def delete_substring(self, pos, length):
for _ in range(length):
self.delete_char(pos) # 逐个删除字符
# 转换为字符串表示
def __str__(self):
current = self.head
result = []
while current is not None:
result.append(current.data)
current = current.next
return ''.join(result)
2.3 块链串
块链串采用链式存储结构,每个结点存储一个字符。其结构体定义如下:
typedef struct snode{
char data; //存储字符
struct snode *next; //指向下一个结点
}LiString;
基本操作实现:
-
初始化:创建一个头结点,其next指针置为NULL。
-
赋值:通过尾插法将字符串常量的每个字符逐个插入到链表中。
-
求子串:从头结点开始遍历链表,找到指定位置的结点,然后逐个复制字符到新的链表中。
-
连接:将两个链表连接起来,形成一个新的链表。
-
插入:在指定位置插入一个子链表。
-
删除:通过调整指针来删除指定位置的子链表。
C语言实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BLOCK_SIZE 5 // 块的大小
// 定义块结构
typedef struct Block {
char data[BLOCK_SIZE]; // 存储字符的数组
struct Block *next; // 指向下一个块的指针
} Block;
// 定义串结构
typedef struct {
Block *head; // 指向第一个块
int length; // 串的总长度
} String;
// 初始化串
void initString(String *s) {
s->head = NULL; // 初始时没有块
s->length = 0; // 初始长度为0
}
// 创建新块
Block* createBlock() {
Block *block = (Block*)malloc(sizeof(Block)); // 分配内存
block->next = NULL; // 初始化next指针为NULL
return block;
}
// 在指定位置插入字符
void insertChar(String *s, int pos, char c) {
if (pos < 0 || pos > s->length) {
printf("Invalid position\n");
return;
}
// 计算块索引和块内位置
int blockIndex = pos / BLOCK_SIZE;
int offset = pos % BLOCK_SIZE;
// 遍历到目标块
Block *current = s->head;
Block *prev = NULL;
for (int i = 0; i < blockIndex; i++) {
if (current == NULL) {
// 如果目标块不存在,创建新块
Block *newBlock = createBlock();
if (prev == NULL) {
s->head = newBlock;
} else {
prev->next = newBlock;
}
current = newBlock;
} else {
prev = current;
current = current->next;
}
}
// 如果当前块已满,创建新块
if (current == NULL || offset >= BLOCK_SIZE) {
Block *newBlock = createBlock();
if (prev == NULL) {
s->head = newBlock;
} else {
prev->next = newBlock;
}
current = newBlock;
offset = 0;
blockIndex++;
}
// 插入字符
for (int i = BLOCK_SIZE - 1; i > offset; i--) {
current->data[i] = current->data[i - 1];
}
current->data[offset] = c;
s->length++;
}
// 删除指定位置的字符
void deleteChar(String *s, int pos) {
if (pos < 0 || pos >= s->length) {
printf("Invalid position\n");
return;
}
// 计算块索引和块内位置
int blockIndex = pos / BLOCK_SIZE;
int offset = pos % BLOCK_SIZE;
// 遍历到目标块
Block *current = s->head;
Block *prev = NULL;
for (int i = 0; i < blockIndex; i++) {
prev = current;
current = current->next;
if (current == NULL) {
printf("Block not found\n");
return;
}
}
// 删除字符
for (int i = offset; i < BLOCK_SIZE - 1; i++) {
current->data[i] = current->data[i + 1];
}
// 如果当前块为空,删除该块
if (current->data[0] == '\0') {
if (prev == NULL) {
s->head = current->next;
} else {
prev->next = current->next;
}
free(current);
}
s->length--;
}
// 查找子串
int findSubstring(String *s, const char *sub) {
int subLen = strlen(sub);
if (subLen == 0) return 0;
int pos = 0;
Block *current = s->head;
while (current != NULL) {
for (int i = 0; i < BLOCK_SIZE && pos < s->length; i++) {
if (current->data[i] == sub[0]) {
// 检查是否匹配子串
int j;
for (j = 1; j < subLen && pos + j < s->length; j++) {
Block *temp = current;
int k = i + 1;
while (k >= BLOCK_SIZE && temp != NULL) {
temp = temp->next;
k -= BLOCK_SIZE;
}
if (temp == NULL || temp->data[k] != sub[j]) {
break;
}
}
if (j == subLen) {
return pos;
}
}
pos++;
}
current = current->next;
}
return -1;
}
// 替换子串
void replaceSubstring(String *s, const char *oldSub, const char *newSub) {
int pos = findSubstring(s, oldSub);
if (pos == -1) return;
int oldLen = strlen(oldSub);
int newLen = strlen(newSub);
// 删除旧子串
for (int i = 0; i < oldLen; i++) {
deleteChar(s, pos);
}
// 插入新子串
for (int i = 0; i < newLen; i++) {
insertChar(s, pos + i, newSub[i]);
}
}
// 释放串的内存
void freeString(String *s) {
Block *current = s->head;
while (current != NULL) {
Block *temp = current;
current = current->next;
free(temp);
}
s->head = NULL;
s->length = 0;
}
// 打印串
void printString(String *s) {
Block *current = s->head;
while (current != NULL) {
for (int i = 0; i < BLOCK_SIZE && i < s->length; i++) {
printf("%c", current->data[i]);
}
current = current->next;
}
printf("\n");
}
// 主函数
int main() {
String s;
initString(&s);
// 插入字符
insertChar(&s, 0, 'H');
insertChar(&s, 1, 'e');
insertChar(&s, 2, 'l');
insertChar(&s, 3, 'l');
insertChar(&s, 4, 'o');
printf("String: ");
printString(&s);
// 删除字符
deleteChar(&s, 4);
printf("After deletion: ");
printString(&s);
// 查找子串
const char *sub = "ll";
int pos = findSubstring(&s, sub);
if (pos != -1) {
printf("Substring found at position: %d\n", pos);
} else {
printf("Substring not found.\n");
}
// 替换子串
const char *oldSub = "ll";
const char *newSub = "mm";
replaceSubstring(&s, oldSub, newSub);
printf("After replacement: ");
printString(&s);
// 释放内存
freeString(&s);
return 0;
}
C++实现
#include <iostream>
#include <cstring>
using namespace std;
#define BLOCK_SIZE 5 // 块的大小
// 定义块类
class Block {
public:
char data[BLOCK_SIZE]; // 存储字符的数组
Block *next; // 指向下一个块的指针
Block() {
next = NULL;
}
};
// 定义串类
class String {
private:
Block *head; // 指向第一个块
int length; // 串的总长度
public:
String() {
head = NULL; // 初始时没有块
length = 0; // 初始长度为0
}
// 在指定位置插入字符
void insertChar(int pos, char c) {
if (pos < 0 || pos > length) {
cout << "Invalid position" << endl;
return;
}
int blockIndex = pos / BLOCK_SIZE;
int offset = pos % BLOCK_SIZE;
Block *current = head;
Block *prev = NULL;
for (int i = 0; i < blockIndex; i++) {
if (current == NULL) {
Block *newBlock = new Block();
if (prev == NULL) {
head = newBlock;
} else {
prev->next = newBlock;
}
current = newBlock;
} else {
prev = current;
current = current->next;
}
}
if (current == NULL || offset >= BLOCK_SIZE) {
Block *newBlock = new Block();
if (prev == NULL) {
head = newBlock;
} else {
prev->next = newBlock;
}
current = newBlock;
offset = 0;
blockIndex++;
}
for (int i = BLOCK_SIZE - 1; i > offset; i--) {
current->data[i] = current->data[i - 1];
}
current->data[offset] = c;
length++;
}
// 删除指定位置的字符
void deleteChar(int pos) {
if (pos < 0 || pos >= length) {
cout << "Invalid position" << endl;
return;
}
int blockIndex = pos / BLOCK_SIZE;
int offset = pos % BLOCK_SIZE;
Block *current = head;
Block *prev = NULL;
for (int i = 0; i < blockIndex; i++) {
prev = current;
current = current->next;
if (current == NULL) {
cout << "Block not found" << endl;
return;
}
}
for (int i = offset; i < BLOCK_SIZE - 1; i++) {
current->data[i] = current->data[i + 1];
}
if (current->data[0] == '\0') {
if (prev == NULL) {
head = current->next;
} else {
prev->next = current->next;
}
delete current;
}
length--;
}
// 查找子串
int findSubstring(const char *sub) {
int subLen = strlen(sub);
if (subLen == 0) return 0;
int pos = 0;
Block *current = head;
while (current != NULL) {
for (int i = 0; i < BLOCK_SIZE && pos < length; i++) {
if (current->data[i] == sub[0]) {
int j;
for (j = 1; j < subLen && pos + j < length; j++) {
Block *temp = current;
int k = i + 1;
while (k >= BLOCK_SIZE && temp != NULL) {
temp = temp->next;
k -= BLOCK_SIZE;
}
if (temp == NULL || temp->data[k] != sub[j]) {
break;
}
}
if (j == subLen) {
return pos;
}
}
pos++;
}
current = current->next;
}
return -1;
}
// 替换子串
void replaceSubstring(const char *oldSub, const char *newSub) {
int pos = findSubstring(oldSub);
if (pos == -1) return;
int oldLen = strlen(oldSub);
int newLen = strlen(newSub);
for (int i = 0; i < oldLen; i++) {
deleteChar(pos);
}
for (int i = 0; i < newLen; i++) {
insertChar(pos + i, newSub[i]);
}
}
// 打印串
void print() {
Block *current = head;
while (current != NULL) {
for (int i = 0; i < BLOCK_SIZE && i < length; i++) {
cout << current->data[i];
}
current = current->next;
}
cout << endl;
}
// 释放内存
~String() {
Block *current = head;
while (current != NULL) {
Block *temp = current;
current = current->next;
delete temp;
}
}
};
int main() {
String s;
s.insertChar(0, 'H');
s.insertChar(1, 'e');
s.insertChar(2, 'l');
s.insertChar(3, 'l');
s.insertChar(4, 'o');
cout << "String: ";
s.print();
s.deleteChar(4);
cout << "After deletion: ";
s.print();
const char *sub = "ll";
int pos = s.findSubstring(sub);
if (pos != -1) {
cout << "Substring found at position: " << pos << endl;
} else {
cout << "Substring not found." << endl;
}
const char *oldSub = "ll";
const char *newSub = "mm";
s.replaceSubstring(oldSub, newSub);
cout << "After replacement: ";
s.print();
return 0;
}
Java实现
public class BlockString {
private static final int BLOCK_SIZE = 5; // 块的大小
// 定义块类
private static class Block {
char[] data = new char[BLOCK_SIZE]; // 存储字符的数组
Block next; // 指向下一个块的指针
Block() {
next = null;
}
}
// 定义串类
private static class String {
private Block head; // 指向第一个块
private int length; // 串的总长度
String() {
head = null; // 初始时没有块
length = 0; // 初始长度为0
}
// 在指定位置插入字符
void insertChar(int pos, char c) {
if (pos < 0 || pos > length) {
System.out.println("Invalid position");
return;
}
int blockIndex = pos / BLOCK_SIZE;
int offset = pos % BLOCK_SIZE;
Block current = head;
Block prev = null;
for (int i = 0; i < blockIndex; i++) {
if (current == null) {
Block newBlock = new Block();
if (prev == null) {
head = newBlock;
} else {
prev.next = newBlock;
}
current = newBlock;
} else {
prev = current;
current = current.next;
}
}
if (current == null || offset >= BLOCK_SIZE) {
Block newBlock = new Block();
if (prev == null) {
head = newBlock;
} else {
prev.next = newBlock;
}
current = newBlock;
offset = 0;
blockIndex++;
}
for (int i = BLOCK_SIZE - 1; i > offset; i--) {
current.data[i] = current.data[i - 1];
}
current.data[offset] = c;
length++;
}
// 删除指定位置的字符
void deleteChar(int pos) {
if (pos < 0 || pos >= length) {
System.out.println("Invalid position");
return;
}
int blockIndex = pos / BLOCK_SIZE;
int offset = pos % BLOCK_SIZE;
Block current = head;
Block prev = null;
for (int i = 0; i < blockIndex; i++) {
prev = current;
current = current.next;
if (current == null) {
System.out.println("Block not found");
return;
}
}
for (int i = offset; i < BLOCK_SIZE - 1; i++) {
current.data[i] = current.data[i + 1];
}
if (current.data[0] == '\0') {
if (prev == null) {
head = current.next;
} else {
prev.next = current.next;
}
}
length--;
}
// 查找子串
int findSubstring(String sub) {
int subLen = sub.length();
if (subLen == 0) return 0;
int pos = 0;
Block current = head;
while (current != null) {
for (int i = 0; i < BLOCK_SIZE && pos < length; i++) {
if (current.data[i] == sub.charAt(0)) {
int j;
for (j = 1; j < subLen && pos + j < length; j++) {
Block temp = current;
int k = i + 1;
while (k >= BLOCK_SIZE && temp != null) {
temp = temp.next;
k -= BLOCK_SIZE;
}
if (temp == null || temp.data[k] != sub.charAt(j)) {
break;
}
}
if (j == subLen) {
return pos;
}
}
pos++;
}
current = current.next;
}
return -1;
}
// 替换子串
void replaceSubstring(String oldSub, String newSub) {
int pos = findSubstring(oldSub);
if (pos == -1) return;
int oldLen = oldSub.length();
int newLen = newSub.length();
for (int i = 0; i < oldLen; i++) {
deleteChar(pos);
}
for (int i = 0; i < newLen; i++) {
insertChar(pos + i, newSub.charAt(i));
}
}
// 打印串
void print() {
Block current = head;
while (current != null) {
for (int i = 0; i < BLOCK_SIZE && i < length; i++) {
System.out.print(current.data[i]);
}
current = current.next;
}
System.out.println();
}
}
public static void main(String[] args) {
String s = new String();
s.insertChar(0, 'H');
s.insertChar(1, 'e');
s.insertChar(2, 'l');
s.insertChar(3, 'l');
s.insertChar(4, 'o');
System.out.print("String: ");
s.print();
s.deleteChar(4);
System.out.print("After deletion: ");
s.print();
String sub = new String();
sub.insertChar(0, 'l');
sub.insertChar(1, 'l');
int pos = s.findSubstring(sub);
if (pos != -1) {
System.out.println("Substring found at position: " + pos);
} else {
System.out.println("Substring not found.");
}
String oldSub = new String();
oldSub.insertChar(0, 'l');
oldSub.insertChar(1, 'l');
String newSub = new String();
newSub.insertChar(0, 'm');
newSub.insertChar(1, 'm');
s.replaceSubstring(oldSub, newSub);
System.out.print("After replacement: ");
s.print();
}
}
Python实现
class Block:
def __init__(self, block_size=5):
self.data = [''] * block_size # 存储字符的数组
self.next = None # 指向下一个块的指针
self.block_size = block_size # 块的大小
class String:
def __init__(self, block_size=5):
self.head = None # 指向第一个块
self.length = 0 # 串的总长度
self.block_size = block_size # 块的大小
def insert_char(self, pos, c):
if pos < 0 or pos > self.length:
print("Invalid position")
return
block_index = pos // self.block_size
offset = pos % self.block_size
current = self.head
prev = None
for i in range(block_index):
if current is None:
new_block = Block(self.block_size)
if prev is None:
self.head = new_block
else:
prev.next = new_block
current = new_block
else:
prev = current
current = current.next
if current is None or offset >= self.block_size:
new_block = Block(self.block_size)
if prev is None:
self.head = new_block
else:
prev.next = new_block
current = new_block
offset = 0
block_index += 1
# 移动字符为新字符腾位置
for i in range(self.block_size - 1, offset, -1):
if i < self.block_size:
current.data[i] = current.data[i - 1]
current.data[offset] = c
self.length += 1
def delete_char(self, pos):
if pos < 0 or pos >= self.length:
print("Invalid position")
return
block_index = pos // self.block_size
offset = pos % self.block_size
current = self.head
prev = None
for i in range(block_index):
prev = current
current = current.next
if current is None:
print("Block not found")
return
# 移动字符填补空缺
for i in range(offset, self.block_size - 1):
current.data[i] = current.data[i + 1]
# 如果当前块为空,删除该块
if all(char == '' for char in current.data):
if prev is None:
self.head = current.next
else:
prev.next = current.next
del current
self.length -= 1
def find_substring(self, sub):
sub_len = len(sub)
if sub_len == 0:
return 0
pos = 0
current = self.head
while current is not None:
for i in range(self.block_size):
if pos >= self.length:
break
if current.data[i] == sub[0]:
match = True
for j in range(1, sub_len):
if pos + j >= self.length:
match = False
break
temp = current
k = i + 1
while k >= self.block_size and temp is not None:
temp = temp.next
k -= self.block_size
if temp is None or k >= self.block_size or temp.data[k] != sub[j]:
match = False
break
if match:
return pos
pos += 1
if pos >= self.length:
break
current = current.next
return -1
def replace_substring(self, old_sub, new_sub):
pos = self.find_substring(old_sub)
if pos == -1:
return
old_len = len(old_sub)
new_len = len(new_sub)
# 删除旧子串
for _ in range(old_len):
self.delete_char(pos)
# 插入新子串
for i in range(new_len):
self.insert_char(pos + i, new_sub[i])
def __str__(self):
result = []
current = self.head
while current is not None:
for i in range(self.block_size):
if len(result) < self.length:
result.append(current.data[i])
current = current.next
return ''.join(result)
def main():
s = String(block_size=5)
s.insert_char(0, 'H')
s.insert_char(1, 'e')
s.insert_char(2, 'l')
s.insert_char(3, 'l')
s.insert_char(4, 'o')
print("String:", s)
s.delete_char(4)
print("After deletion:", s)
sub = "ll"
pos = s.find_substring(sub)
if pos != -1:
print(f"Substring '{sub}' found at position: {pos}")
else:
print(f"Substring '{sub}' not found.")
old_sub = "ll"
new_sub = "mm"
s.replace_substring(old_sub, new_sub)
print("After replacement:", s)
if __name__ == "__main__":
main()
三、串的应用举例:简单的行编辑器
行编辑器是一种以行为单位进行编辑的文本编辑程序。我们可以使用串的数据结构来实现一个简单的行编辑器,具备行插入、行删除等基本功能。
3.1 行编辑器的功能
-
行插入:在指定行号之后插入一行文本。
-
行删除:删除指定行号的行,或者删除指定范围内的行。
-
活区切换:将当前活区的内容写入输出文件,并从输入文件中读入下一段作为新的活区。
-
活区显示:逐页显示活区的内容,每页显示若干行。
3.2 行编辑器的实现
我们可以使用链表来存储每一行的文本,每个结点存储一行的内容。通过指针操作来实现行的插入和删除。以下是部分代码示例:
typedef struct text{
char string[80]; //存储每一行的元素
struct text *next; //指向后一个节点的指针
struct text *pre; //指向前一个节点的指针
int num; //每一行元素的长度
int flat; //确定此行是否被删除的标志
}text;
-
行插入:在指定行号之后插入一行文本。
int insert(text *head, int hang, char *content){
text *p = head;
for(int i = 0; i < hang - 1; i++){
p = p->next;
}
text *newNode = (text *)malloc(sizeof(text));
newNode->num = strlen(content);
strcpy(newNode->string, content);
newNode->flat = 1;
newNode->next = p->next;
newNode->pre = p;
p->next->pre = newNode;
p->next = newNode;
return 1;
}
-
行删除:删除指定行号的行。
int del(text *head, int hang){
text *p = head;
for(int i = 0; i < hang; i++){
p = p->next;
}
p->pre->next = p->next;
p->next->pre = p->pre;
free(p);
return 1;
}
C语言实现
// 引入标准输入输出头文件,用于使用printf和scanf等函数
#include <stdio.h>
// 引入字符串处理头文件,用于使用strlen等字符串处理函数
#include <string.h>
// 定义一个常量MAX_LEN,表示串的最大长度为100
#define MAX_LEN 100
// 定义串的结构体,使用typedef为结构体起别名String
typedef struct {
// 字符数组,用于存储串的内容,最大长度为MAX_LEN
char data[MAX_LEN];
// 整型变量,记录当前串的实际长度
int length;
} String;
// 初始化串的函数,接收一个指向String结构体的指针
void initString(String *s) {
// 将串的长度初始化为0,表示空串
s->length = 0;
}
// 在指定位置插入字符的函数,接收一个指向String结构体的指针、插入位置和要插入的字符
void insertChar(String *s, int pos, char c) {
// 检查插入位置是否合法(在0到当前串长度之间)且串还有剩余空间
if (pos >= 0 && pos <= s->length && s->length < MAX_LEN) {
// 从串的末尾开始,将插入位置及之后的字符依次向后移动一位
for (int i = s->length; i > pos; i--) {
s->data[i] = s->data[i-1];
}
// 在指定位置插入字符
s->data[pos] = c;
// 串的长度加1
s->length++;
}
}
// 删除指定位置字符的函数,接收一个指向String结构体的指针和要删除的位置
void deleteChar(String *s, int pos) {
// 检查删除位置是否合法(在0到当前串长度减1之间)
if (pos >= 0 && pos < s->length) {
// 从删除位置开始,将后面的字符依次向前移动一位
for (int i = pos; i < s->length - 1; i++) {
s->data[i] = s->data[i+1];
}
// 串的长度减1
s->length--;
}
}
// 查找子串的函数,接收一个指向String结构体的指针和要查找的子串
int findSubstring(String *s, const char *sub) {
// 计算子串的长度
int subLen = strlen(sub);
// 遍历主串,检查是否存在子串
for (int i = 0; i <= s->length - subLen; i++) {
int j;
// 逐字符比较主串从位置i开始的子串与要查找的子串
for (j = 0; j < subLen; j++) {
if (s->data[i + j] != sub[j]) {
// 若有不相等的字符,跳出内层循环
break;
}
}
if (j == subLen) {
// 若内层循环正常结束,说明找到了子串,返回其起始位置
return i;
}
}
// 若未找到子串,返回-1
return -1;
}
// 替换子串的函数,接收一个指向String结构体的指针、要替换的旧子串和新子串
void replaceSubstring(String *s, const char *oldSub, const char *newSub) {
// 查找旧子串在主串中的位置
int pos = findSubstring(s, oldSub);
if (pos != -1) {
// 计算旧子串的长度
int oldLen = strlen(oldSub);
// 计算新子串的长度
int newLen = strlen(newSub);
// 若新子串长度大于旧子串长度
if (newLen > oldLen) {
// 检查替换后是否会超出串的最大长度
if (s->length + newLen - oldLen > MAX_LEN) {
// 若超出,输出提示信息并返回
printf("Replacement exceeds maximum length.\n");
return;
}
// 从串的末尾开始,将插入位置及之后的字符依次向后移动新旧子串长度差的位数
for (int i = s->length - 1; i >= pos + oldLen; i--) {
s->data[i + newLen - oldLen] = s->data[i];
}
} else if (newLen < oldLen) {
// 若新子串长度小于旧子串长度,将多余的字符向前移动
for (int i = pos + oldLen - 1; i >= pos + newLen; i--) {
s->data[i] = s->data[i + oldLen - newLen];
}
}
// 将新子串复制到指定位置
for (int i = 0; i < newLen; i++) {
s->data[pos + i] = newSub[i];
}
// 更新串的长度
s->length += newLen - oldLen;
}
}
// 主函数,程序的入口点
int main() {
// 定义一个String类型的变量s
String s;
// 调用初始化函数,将串s初始化为空串
initString(&s);
// 依次在串s的开头插入字符'H' 'e' 'l' 'l' 'o'
insertChar(&s, 0, 'H');
insertChar(&s, 1, 'e');
insertChar(&s, 2, 'l');
insertChar(&s, 3, 'l');
insertChar(&s, 4, 'o');
// 输出当前串的内容
printf("String: %.*s\n", s.length, s.data);
// 删除串s中位置为4的字符
deleteChar(&s, 4);
// 输出删除字符后的串的内容
printf("After deletion: %.*s\n", s.length, s.data);
// 定义要查找的子串
const char *sub = "ll";
// 查找子串在串s中的位置
int pos = findSubstring(&s, sub);
if (pos != -1) {
// 若找到,输出子串的起始位置
printf("Substring found at position: %d\n", pos);
} else {
// 若未找到,输出未找到的提示信息
printf("Substring not found.\n");
}
// 定义要替换的旧子串和新子串
const char *oldSub = "ll";
const char *newSub = "mm";
// 调用替换子串的函数,将旧子串替换为新子串
replaceSubstring(&s, oldSub, newSub);
// 输出替换后的串的内容
printf("After replacement: %.*s\n", s.length, s.data);
// 程序正常结束,返回0
return 0;
}
C++实现
// 引入输入输出流头文件,用于标准输入输出操作
#include <iostream>
// 引入字符串处理头文件,用于使用标准库中的字符串类和相关函数
#include <string>
// 使用标准命名空间,这样可以直接使用标准库中的函数和类,而无需加std::前缀
using namespace std;
// 自定义字符串类String
class String {
private:
// 指向字符数组的指针,用于存储字符串内容
char *data;
// 当前字符串的长度
int length;
// 字符数组的容量,即当前分配的内存大小
int capacity;
public:
// 构造函数,使用初始化列表初始化length为0,capacity为100
String() : length(0), capacity(100) {
// 为字符数组分配capacity大小的内存空间
data = new char[capacity];
}
// 向字符串指定位置插入一个字符的函数
void insertChar(int pos, char c) {
// 检查插入位置是否合法,必须在0到length之间(包含两端)
if (pos >= 0 && pos <= length) {
// 检查当前字符串长度是否达到容量上限
if (length == capacity) {
// 若达到上限,将容量扩大为原来的2倍
capacity *= 2;
// 为新的字符数组分配扩大后的容量大小的内存空间
char *newData = new char[capacity];
// 将原字符数组中的内容复制到新的字符数组中
for (int i = 0; i < length; i++) {
newData[i] = data[i];
}
// 释放原字符数组占用的内存空间
delete[] data;
// 让data指针指向新的字符数组
data = newData;
}
// 将插入位置及之后的字符依次向后移动一位
for (int i = length; i > pos; i--) {
data[i] = data[i-1];
}
// 在指定位置插入新字符
data[pos] = c;
// 字符串长度加1
length++;
}
}
// 删除字符串指定位置字符的函数
void deleteChar(int pos) {
// 检查删除位置是否合法,必须在0到length-1之间
if (pos >= 0 && pos < length) {
// 将删除位置之后的字符依次向前移动一位
for (int i = pos; i < length - 1; i++) {
data[i] = data[i+1];
}
// 字符串长度减1
length--;
}
}
// 查找子字符串在当前字符串中首次出现位置的函数
int findSubstring(const char *sub) {
// 计算子字符串的长度
int subLen = strlen(sub);
// 遍历当前字符串,检查是否存在子字符串
for (int i = 0; i <= length - subLen; i++) {
int j;
// 逐字符比较当前字符串从位置i开始的子串与目标子字符串
for (j = 0; j < subLen; j++) {
if (data[i + j] != sub[j]) {
// 若有不相等的字符,跳出内层循环
break;
}
}
if (j == subLen) {
// 若内层循环正常结束,说明找到了子字符串,返回其起始位置
return i;
}
}
// 若未找到子字符串,返回-1
return -1;
}
// 替换字符串中指定子字符串的函数
void replaceSubstring(const char *oldSub, const char *newSub) {
// 查找旧子字符串在当前字符串中的位置
int pos = findSubstring(oldSub);
if (pos != -1) {
// 计算旧子字符串的长度
int oldLen = strlen(oldSub);
// 计算新子字符串的长度
int newLen = strlen(newSub);
// 若新子字符串长度大于旧子字符串长度
if (newLen > oldLen) {
// 检查是否需要扩容
if (length + newLen - oldLen > capacity) {
// 若需要,将容量调整为长度加上新旧子字符串长度差再加上100
capacity = length + newLen - oldLen + 100;
// 为新的字符数组分配扩大后的容量大小的内存空间
char *newData = new char[capacity];
// 将原字符数组中的内容复制到新的字符数组中
for (int i = 0; i < length; i++) {
newData[i] = data[i];
}
// 释放原字符数组占用的内存空间
delete[] data;
// 让data指针指向新的字符数组
data = newData;
}
// 将插入位置之后的字符依次向后移动新旧子字符串长度差的位数
for (int i = length - 1; i >= pos + oldLen; i--) {
data[i + newLen - oldLen] = data[i];
}
} else if (newLen < oldLen) {
// 若新子字符串长度小于旧子字符串长度,将多余的字符向前移动
for (int i = pos + oldLen - 1; i >= pos + newLen; i--) {
data[i] = data[i + oldLen - newLen];
}
}
// 将新子字符串复制到指定位置
for (int i = 0; i < newLen; i++) {
data[pos + i] = newSub[i];
}
// 更新字符串长度
length += newLen - oldLen;
}
}
// 析构函数,用于释放字符数组占用的内存空间
~String() {
delete[] data;
}
// 将自定义字符串类转换为标准库字符串类的函数
string toString() {
return string(data, length);
}
};
// 主函数,程序的入口点
int main() {
// 创建一个自定义字符串类的对象
String s;
// 依次在字符串开头插入字符'H' 'e' 'l' 'l' 'o'
s.insertChar(0, 'H');
s.insertChar(1, 'e');
s.insertChar(2, 'l');
s.insertChar(3, 'l');
s.insertChar(4, 'o');
// 输出当前字符串
cout << "String: " << s.toString() << endl;
// 删除字符串中位置为4的字符
s.deleteChar(4);
// 输出删除字符后的字符串
cout << "After deletion: " << s.toString() << endl;
// 定义要查找的子字符串
const char *sub = "ll";
// 查找子字符串在当前字符串中的位置
int pos = s.findSubstring(sub);
if (pos != -1) {
// 若找到,输出子字符串的起始位置
cout << "Substring found at position: " << pos << endl;
} else {
// 若未找到,输出未找到的提示信息
cout << "Substring not found." << endl;
}
// 定义要替换的旧子字符串和新子字符串
const char *oldSub = "ll";
const char *newSub = "mm";
// 替换字符串中的旧子字符串为新子字符串
s.replaceSubstring(oldSub, newSub);
// 输出替换后的字符串
cout << "After replacement: " << s.toString() << endl;
// 程序正常结束,返回0
return 0;
}
Java实现
```java
// 定义一个名为StringEditor的类,用于实现字符串的编辑功能
public class StringEditor {
// 定义一个私有的内部类Node,它代表链表中的一个节点
private class Node {
// 节点中存储的字符数据
char data;
// 指向下一个节点的引用,用于构建链表结构
Node next;
}
// 链表的头节点,作为链表操作的起始点
private Node head;
// 记录当前链表所表示字符串的长度
private int length;
// 类的构造函数,用于初始化StringEditor对象
public StringEditor() {
// 初始化头节点为null,表示链表为空
head = null;
// 初始化字符串长度为0
length = 0;
}
// 该方法用于在指定位置插入一个字符
public void insertChar(int pos, char c) {
// 检查插入位置是否合法,位置需在0到length之间(包含0和length)
if (pos >= 0 && pos <= length) {
// 创建一个新的节点来存储要插入的字符
Node newNode = new Node();
// 将字符c赋值给新节点的数据域
newNode.data = c;
// 如果插入位置为0,即要在链表头部插入
if (pos == 0) {
// 让新节点的下一个节点指向原来的头节点
newNode.next = head;
// 更新头节点为新节点
head = newNode;
} else {
// 定义一个当前节点,初始化为头节点,用于遍历链表
Node current = head;
// 循环找到插入位置的前一个节点
for (int i = 0; i < pos - 1; i++) {
// 移动到下一个节点
current = current.next;
}
// 让新节点的下一个节点指向当前节点的下一个节点
newNode.next = current.next;
// 让当前节点的下一个节点指向新节点
current.next = newNode;
}
// 插入成功后,字符串长度加1
length++;
}
}
// 该方法用于删除指定位置的字符
public void deleteChar(int pos) {
// 检查删除位置是否合法,位置需在0到length - 1之间
if (pos >= 0 && pos < length) {
// 如果删除位置为0,即要删除头节点
if (pos == 0) {
// 直接将头节点更新为原来头节点的下一个节点
head = head.next;
} else {
// 定义一个当前节点,初始化为头节点,用于遍历链表
Node current = head;
// 循环找到删除位置的前一个节点
for (int i = 0; i < pos - 1; i++) {
// 移动到下一个节点
current = current.next;
}
// 跳过要删除的节点,让当前节点的下一个节点指向要删除节点的下一个节点
current.next = current.next.next;
}
// 删除成功后,字符串长度减1
length--;
}
}
// 该方法用于查找指定子字符串在链表表示的字符串中首次出现的位置
public int findSubstring(String sub) {
// 定义一个当前节点,初始化为头节点,用于遍历链表
Node current = head;
// 创建一个StringBuilder对象,用于将链表中的字符拼接成字符串
StringBuilder sb = new StringBuilder();
// 遍历链表,将每个节点的字符添加到StringBuilder中
while (current != null) {
// 添加当前节点的字符
sb.append(current.data);
// 移动到下一个节点
current = current.next;
}
// 将StringBuilder转换为字符串,并调用indexOf方法查找子字符串的位置
return sb.toString().indexOf(sub);
}
// 该方法用于将链表中指定的旧子字符串替换为新子字符串
public void replaceSubstring(String oldSub, String newSub) {
// 调用findSubstring方法查找旧子字符串的位置
int pos = findSubstring(oldSub);
// 如果找到了旧子字符串
if (pos != -1) {
// 调用deleteSubstring方法删除旧子字符串
deleteSubstring(pos, oldSub.length());
// 从新子字符串的末尾开始,依次插入每个字符
for (int i = newSub.length() - 1; i >= 0; i--) {
// 在旧子字符串原来的位置插入新子字符串的字符
insertChar(pos, newSub.charAt(i));
}
}
}
// 该方法用于删除指定位置开始的指定长度的子字符串,是一个私有方法
private void deleteSubstring(int pos, int len) {
// 循环len次,每次删除指定位置的字符
for (int i = 0; i < len; i++) {
// 调用deleteChar方法删除指定位置的字符
deleteChar(pos);
}
}
// 重写toString方法,用于将链表表示的字符串转换为普通的字符串对象
public String toString() {
// 定义一个当前节点,初始化为头节点,用于遍历链表
Node current = head;
// 创建一个StringBuilder对象,用于将链表中的字符拼接成字符串
StringBuilder sb = new StringBuilder();
// 遍历链表,将每个节点的字符添加到StringBuilder中
while (current != null) {
// 添加当前节点的字符
sb.append(current.data);
// 移动到下一个节点
current = current.next;
}
// 将StringBuilder转换为字符串并返回
return sb.toString();
}
// 主方法,程序的入口点
public static void main(String[] args) {
// 创建一个StringEditor对象
StringEditor editor = new StringEditor();
// 在位置0插入字符'H'
editor.insertChar(0, 'H');
// 在位置1插入字符'e'
editor.insertChar(1, 'e');
// 在位置2插入字符'l'
editor.insertChar(2, 'l');
// 在位置3插入字符'l'
editor.insertChar(3, 'l');
// 在位置4插入字符'o'
editor.insertChar(4, 'o');
// 输出当前链表表示的字符串
System.out.println("String: " + editor.toString());
// 删除位置4的字符
editor.deleteChar(4);
// 输出删除字符后的字符串
System.out.println("After deletion: " + editor.toString());
// 定义要查找的子字符串
String sub = "ll";
// 调用findSubstring方法查找子字符串的位置
int pos = editor.findSubstring(sub);
// 如果找到了子字符串
if (pos != -1) {
// 输出子字符串的位置
System.out.println("Substring found at position: " + pos);
} else {
// 输出未找到子字符串的信息
System.out.println("Substring not found.");
}
// 定义要替换的旧子字符串
String oldSub = "ll";
// 定义要替换的新子字符串
String newSub = "mm";
// 调用replaceSubstring方法进行替换
editor.replaceSubstring(oldSub, newSub);
// 输出替换后的字符串
System.out.println("After replacement: " + editor.toString());
}
}
```
Python实现
# 定义一个字符串操作类,模拟基础字符串编辑器功能
class String:
def __init__(self):
# 初始化字符数据容器(Python列表实现)
self.data = []
# 记录当前字符串长度,避免频繁计算len()
self.length = 0
# 字符插入方法
def insert_char(self, pos, c):
# 有效性校验:插入位置需在[0,当前长度]区间
if 0 <= pos <= self.length:
# 使用列表的insert方法实现O(n)时间复杂度的插入
self.data.insert(pos, c)
# 维护长度变量,避免调用len()带来的性能损耗
self.length += 1
# 字符删除方法
def delete_char(self, pos):
# 有效性校验:删除位置需在[0,长度)区间
if 0 <= pos < self.length:
# 使用del语句实现指定位置元素删除
del self.data[pos]
# 同步更新长度计数器
self.length -= 1
# 子字符串查找方法
def find_substring(self, sub):
# 将字符列表转换为字符串(时间复杂度O(n))
# 调用字符串原生find方法实现子串搜索(Boyer-Moore算法)
return ''.join(self.data).find(sub)
# 子字符串替换方法
def replace_substring(self, old_sub, new_sub):
# 先查找旧子串的起始位置
pos = self.find_substring(old_sub)
if pos != -1:
# 删除旧子串(需处理连续删除时的位置偏移)
self.delete_substring(pos, len(old_sub))
# 反向插入新子串:保证插入顺序不影响位置计算
# 例如插入"mm"时,先插入'm'再插入'm',保持正确顺序
for i in reversed(new_sub):
self.insert_char(pos, i)
# 子字符串删除辅助方法
def delete_substring(self, pos, length):
# 执行指定次数的单字符删除
# 由于每次删除后后续元素位置会自动前移,因此固定pos即可
for _ in range(length):
self.delete_char(pos)
# 重写字符串表示方法
def __str__(self):
# 高效拼接字符列表为字符串(时间复杂度O(n))
return ''.join(self.data)
# 主测试函数
def main():
s = String()
# 构建初始字符串(演示连续插入)
s.insert_char(0, 'H') # 创建基础字符串
s.insert_char(1, 'e') # 尾部追加
s.insert_char(2, 'l') # 逐步构建
s.insert_char(3, 'l') # 连续插入
s.insert_char(4, 'o') # 完成"Hello"构造
print("String:", s) # 预期输出:Hello
# 删除末尾字符演示
s.delete_char(4) # 删除索引4(原第5个字符'o')
print("After deletion:", s) # 预期输出:Hell
# 子字符串查找测试
sub = "ll"
pos = s.find_substring(sub)
if pos != -1:
print(f"Substring '{sub}' found at position: {pos}") # 预期找到位置2
else:
print(f"Substring '{sub}' not found.")
# 子字符串替换演示
old_sub = "ll"
new_sub = "mm"
s.replace_substring(old_sub, new_sub) # 将"ll"替换为"mm"
print("After replacement:", s) # 预期输出:Hmm
if __name__ == "__main__":
main()
四、总结核心知识点
4.1 核心知识点梳理
-
串的基本概念:包括串的定义、子串与主串、空串与空格串、串相等等。
-
串的存储实现:定长顺序串、堆串和快链串的存储结构及基本操作实现。
-
串的应用:通过实现简单的行编辑器,展示了串在实际问题中的应用。
4.2 时间性能分析
操作 | 定长顺序串 | 堆串 | 块链串 |
---|---|---|---|
插入字符 | O(n) | O(n) | O(1) |
删除字符 | O(n) | O(n) | O(1) |
查找子串 | O(n) | O(n) | O(n) |
替换子串 | O(n) | O(n) | O(n) |
4.3 空间性能分析
存储方式 | 空间复杂度 | 适用场景 |
---|---|---|
定长顺序串 | O(1) | 长度固定 |
堆串 | O(n) | 长度不固定 |
块链串 | O(n) | 频繁插入和删除 |
4.4 综合性能分析
存储方式 | 时间性能 | 空间性能 | 适用场景 |
---|---|---|---|
定长顺序串 | 插入、删除操作时间复杂度较高,为O(n),其中n为串长;查找、连接等操作时间复杂度较低,为O(1)或O(m),其中m为操作涉及的子串长度 | 需要预先分配固定大小的存储空间,空间利用率可能较低 | 适用于串长较短且操作较为简单的场景 |
堆串 | 插入、删除、连接等操作时间复杂度为O(n),其中n为串长;查找操作时间复杂度较低,为O(1)或O(m) | 动态分配存储空间,空间利用率较高 | 适用于串长较长且操作较为复杂的场景 |
快链串 | 插入、删除操作时间复杂度为O(1),查找操作时间复杂度为O(n),其中n为串长 |
4.5应用场景
存储方式 | 适用场景 |
---|---|
定长顺序串 | 文本编辑器、固定长度字符串存储 |
堆串 | 动态文本处理、模式匹配 |
快链串 | 频繁插入和删除的文本编辑器 |
五、总结
本文详细讲解了串的基本概念、存储实现、应用举例以及性能分析。通过多种编程语言的代码实现,帮助读者更好地理解和实践串的使用。串作为一种基础数据结构,在文本处理和模式匹配等领域有着广泛的应用,掌握其核心知识点对于解决实际问题具有重要意义。