对字符串最常见的操作之一是附加或串联。追加到一个字符串的末尾时的字符串存储在传统的方式(即一个字符数组)将至少需要O(n)的时间(其中N是原始字符串的长度)。
我们可以减少使用绳索数据结构附加的时间。
绳索是一种二叉树结构,其中除叶节点外,每个节点都包含该节点左侧存在的字符数。叶子节点包含实际的字符串,这些字符串分成子字符串(这些子字符串的大小可以由用户确定)。
考虑下图。
该图显示了字符串如何存储在内存中。每个叶节点都包含原始字符串的子字符串,而所有其他节点都包含该节点左侧出现的字符数。将字符数存储在左侧的想法是最大程度地降低查找第i个位置上存在的字符的成本。
好处
绳索大大减少了增加两根字符串的费用。
2.与数组不同,绳索不需要大量连续的内存分配。
3.绳索不需要O(n)额外的内存来执行诸如插入/删除/搜索之类的操作。
4.如果用户要撤消最后的串联,则可以在O(1)时间内通过删除树的根节点来撤消。
缺点
1.源代码的复杂性增加。
2.出现错误的机会更大。
3.存储父节点需要额外的内存。
4.访问第i个字符增加。
现在让我们看一下一种情况,该情况说明了为什么Ropes可以很好地替代整体字符串数组。
给定两个字符串a []和b []。将它们连接在第三个字符串c []中。
例子:
Input : a[] = "This is ", b[] = "an apple"
Output : "This is an apple"
Input : a[] = "This is ", b[] = "geeksforgeeks"
Output : "This is geeksforgeeks"
方法1(天真方法)
我们创建一个字符串c []来存储串联的字符串。我们首先遍历a []并将a []的所有字符复制到c []。然后我们将b []的所有字符复制到c []。
C++
// Simple C++ program to concatenate two strings
#include
using namespace std;
// Function that concatenates strings a[0..n1-1]
// and b[0..n2-1] and stores the result in c[]
void concatenate(char a[], char b[], char c[],
int n1, int n2)
{
// Copy characters of A[] to C[]
int i;
for (i=0; i
Java
//Java program to concatenate two strings
class GFG {
// Function that concatenates strings a[0..n1-1]
// and b[0..n2-1] and stores the result in c[]
static void concatenate(char a[], char b[], char c[],
int n1, int n2) {
// Copy characters of A[] to C[]
int i;
for (i = 0; i < n1; i++) {
c[i] = a[i];
}
// Copy characters of B[]
for (int j = 0; j < n2; j++) {
c[i++] = b[j];
}
}
// Driver code
public static void main(String[] args) {
char a[] = "Hi This is geeksforgeeks. ".toCharArray();
int n1 = a.length;
char b[] = "You are welcome here.".toCharArray();
int n2 = b.length;
// Concatenate a[] and b[] and store result
// in c[]
char c[] = new char[n1 + n2];
concatenate(a, b, c, n1, n2);
for (int i = 0; i < n1 + n2 - 1; i++) {
System.out.print(c[i]);
}
}
}
// This code is contributed by PrinciRaj1992
Python3
# Python3 program to concatenate two strings
# Function that concatenates strings a[0..n1-1]
# and b[0..n2-1] and stores the result in c[]
def concatenate(a, b, c, n1, n2):
# Copy characters of A[] to C[]
i = -1
for i in range(n1):
c[i] = a[i]
# Copy characters of B[]
for j in range(n2):
c[i] = b[j]
i += 1
# Driver Code
if __name__ == "__main__":
a = "Hi This is geeksforgeeks. "
n1 = len(a)
b = "You are welcome here."
n2 = len(b)
a = list(a)
b = list(b)
# Concatenate a[] and b[] and
# store result in c[]
c = [0] * (n1 + n2 - 1)
concatenate(a, b, c, n1, n2)
for i in c:
print(i, end = "")
# This code is conributed by
# sanjeev2552
C#
// C# program to concatenate two strings
using System;
public class GFG {
// Function that concatenates strings a[0..n1-1]
// and b[0..n2-1] and stores the result in c[]
static void concatenate(char []a, char []b, char []c,
int n1, int n2) {
// Copy characters of A[] to C[]
int i;
for (i = 0; i < n1; i++) {
c[i] = a[i];
}
// Copy characters of B[]
for (int j = 0; j < n2; j++) {
c[i++] = b[j];
}
}
// Driver code
public static void Main() {
char []a = "Hi This is geeksforgeeks. ".ToCharArray();
int n1 = a.Length;
char []b = "You are welcome here.".ToCharArray();
int n2 = b.Length;
// Concatenate a[] and b[] and store result
// in c[]
char []c = new char[n1 + n2];
concatenate(a, b, c, n1, n2);
for (int i = 0; i < n1 + n2 - 1; i++) {
Console.Write(c[i]);
}
}
}
/*This code is contributed by PrinciRaj1992*/
输出:
Hi This is geeksforgeeks. You are welcome here
时间复杂度: O(n)
现在,让我们尝试使用绳索解决相同的问题。
方法2(绳索构造方法)
该绳索结构可用于在恒定时间内连接两个字符串。
1.创建一个新的根节点(存储新的串联字符串的根)
2.标记该节点的左子节点,即第一个出现的字符串的根。
3.标记此节点的右子节点,即第二个出现的字符串的根。
就是这样。由于此方法仅需要创建一个新节点,因此其复杂度为O(1) 。
考虑下面的图片(图片来源:https://en.wikipedia.org/wiki/Rope_(data_structure))
// C++ program to concatenate two strings using
// rope data structure.
#include
using namespace std;
// Maximum no. of characters to be put in leaf nodes
const int LEAF_LEN = 2;
// Rope structure
class Rope
{
public:
Rope *left, *right, *parent;
char *str;
int lCount;
};
// Function that creates a Rope structure.
// node --> Reference to pointer of current root node
// l --> Left index of current substring (initially 0)
// r --> Right index of current substring (initially n-1)
// par --> Parent of current node (Initially NULL)
void createRopeStructure(Rope *&node, Rope *par,
char a[], int l, int r)
{
Rope *tmp = new Rope();
tmp->left = tmp->right = NULL;
// We put half nodes in left subtree
tmp->parent = par;
// If string length is more
if ((r-l) > LEAF_LEN)
{
tmp->str = NULL;
tmp->lCount = (r-l)/2;
node = tmp;
int m = (l + r)/2;
createRopeStructure(node->left, node, a, l, m);
createRopeStructure(node->right, node, a, m+1, r);
}
else
{
node = tmp;
tmp->lCount = (r-l);
int j = 0;
tmp->str = new char[LEAF_LEN];
for (int i=l; i<=r; i++)
tmp->str[j++] = a[i];
}
}
// Function that prints the string (leaf nodes)
void printstring(Rope *r)
{
if (r==NULL)
return;
if (r->left==NULL && r->right==NULL)
cout << r->str;
printstring(r->left);
printstring(r->right);
}
// Function that efficiently concatenates two strings
// with roots root1 and root2 respectively. n1 is size of
// string represented by root1.
// root3 is going to store root of concatenated Rope.
void concatenate(Rope *&root3, Rope *root1, Rope *root2, int n1)
{
// Create a new Rope node, and make root1
// and root2 as children of tmp.
Rope *tmp = new Rope();
tmp->parent = NULL;
tmp->left = root1;
tmp->right = root2;
root1->parent = root2->parent = tmp;
tmp->lCount = n1;
// Make string of tmp empty and update
// reference r
tmp->str = NULL;
root3 = tmp;
}
// Driver code
int main()
{
// Create a Rope tree for first string
Rope *root1 = NULL;
char a[] = "Hi This is geeksforgeeks. ";
int n1 = sizeof(a)/sizeof(a[0]);
createRopeStructure(root1, NULL, a, 0, n1-1);
// Create a Rope tree for second string
Rope *root2 = NULL;
char b[] = "You are welcome here.";
int n2 = sizeof(b)/sizeof(b[0]);
createRopeStructure(root2, NULL, b, 0, n2-1);
// Concatenate the two strings in root3.
Rope *root3 = NULL;
concatenate(root3, root1, root2, n1);
// Print the new concatenated string
printstring(root3);
cout << endl;
return 0;
}
输出:
Hi This is geeksforgeeks. You are welcome here.