算法原理与实践(数组和字符串)

Array & String

大纲

  1. 入门题 string match
  2. Array 中 HashTable 的应用
  3. C/C++ 中的 string
  4. 例题分析

part 1 入门题

  在 source(母串)中,寻找 target(子串) 是否出现。

/* Returns the position of the first occurrence of string target in
string source, or -1 if target is not part of source.*/
int strStr(String source, String target) {
    //…
}

  字符串匹配

  两种比较易于实现的字符串比较算法:

  假设在长度为 n 的母串中匹配长度为 m 的子串。(http://en.wikipedia.org/wiki/String_searching_algorithm)。

  1. Brute-Force 算法: 顺序遍历母串,将每个字符作为匹配的起始字符,判断是否匹配子串。时间复杂度 O(m*n)

char* strStr(const char *str, const char *target)
{
    if(!*target)    return str;

    char *p1 = (char*)str;

    while(*p1)
    {
        char *p1Begin = p1, *p2 = (char*)target;

        while(*p1 && *p2 && *p1 == *p2)
        {
            p1++;
            p2++;
        }

        if(!*p2)    return p1Begin;

        p1 = p1Begin + 1;
    }
    return NULL;
}

  2.Rabin-Karp 算法 :将每一个匹配子串映射为一个hash值。例如,将子串看做一个多进制数,比较它的值与母串中相同长度子串的hash值,如果相同,再细致地按字符确认字符串是否确实相同。顺序计算母串hash值的过程中,使用增量计算的方法:扣除最高位的hash值,增加最低位的hash值。因此能在平均情况下做到O(m+n)。

#include <string>
#include <iostream>
#include <functional>

using namespace std;

size_t Rabin_karp_StrStr(string &source, string &target)
{
    hash<string> strHash;

    size_t targetHash = strHash(target);

    for (size_t i = 0; i < source.size() - target.size() + 1; i++)
    {
        string subSource = source.substr(i, target.size());
        if (strHash(subSource) == targetHash)
        {
            if (subSource == target)
                return i;
        }
    }

    return 0;
}

int main()
{
    string s1 = "abcd";
    string s2 = "cd";

    cout << Rabin_karp_StrStr(s1, s2);

    return 0;
}

part 2 Array

int array[arraySize]; //在Stack上定义长度为arraySize的整型数组
int *array = new int[arraySize]; //在Heap上定义长度为arraySize的整型数组
delete[] array; //使用完后需要释放内存:

  注意,在旧的编译器中,不能在Stack上定义一个长度不确定的数组,即只能定义如下:
  int array[10];
  新的编译器没有如上限制。但是如果数组长度不定,则不能初始化数组:
  int array[arraySize] = {0}; //把不定长度的数组初始化为零,编译报错。

part 3 工具箱:Stack Vs. Heap
  Stack主要是指由操作系统自动管理的内存空间。当进入一个函数,操作系统会为该函数中的局部变量分配储存空间。事实上,系统会分配一个内存块,叠加在当前的stack上,并且利用指针指向前一个内存块的地址。
  函数的局部变量就存储在当前的内存块上。当该函数返回时,系统“弹出”内存块,并且根据指针回到前一个内存块。所以,Stack总是以后进先出(LIFO)的方式工作
  Heap是用来储存动态分配变量的空间。对于heap而言,并没有像stack那样后进先出的规则,程序员可以选择随时分配或回收内存。这就意味着需要程序员自己用命令回收内存,否则会产生内存泄露(memory leak)。
  在C/C++中,程序员需要调用free/delete来释放动态分配的内存。在JAVA,Objective-C (with Automatic Reference Count)中,语言本身引入垃圾回收和计数规则帮助用户决定在什么时候自动释放内存。

part 4 二维数组

//在Stack上创建:
int array[M][N];

//传递给子函数:
void func(int arr[M][N])
{
    /* M可以省略,但N必须存在,以便编译器确定移动内存的间距 */
}

//在Heap上创建:
int **array = new int*[M]; // 或者 (int**)malloc( M * sizeof(int*) );
for( int i = 0; i < M; i++)
    array [i] = new int[N]; // 或者 (int*)malloc( N * sizeof(int) );

//传递给子函数:
void func( int **arr, int M, int N ){}

//使用完后需要释放内存:
for( int i = 0; i < M; i++)
    delete[] array[i];
delete[] array;

part 5 工具箱:Vector

  vector可以用运算符[]直接访问元素。请参考http://www.cplusplus.com/reference/vector/vector/

size_type size() const; // Returns the number of elements in the vector.
void push_back (const value_type& val);
void pop_back();
iterator erase (iterator first, iterator last); // Removes from the vector either a singleelement (position) or a range of elements ([first,last)).

for (vector<int>::iterator it = v.begin(); it != v.end(); )
{
    if (condition)
    {
        it = v.erase(it);
    }
    else
    {
        ++it;
    }
}

part 6 Hash Table

  Hash table 几乎是最为重要的数据结构,主要用于基于“key”的查找,存储的基本元素是 key-value 的 pair。逻辑上,数组可以作为 Hash table 的一个特例:key是一个非负整数。

  Operations

  • Insert - O(1)
  • Delete - O(1)
  • Find - O(1)

  哈希碰撞 - Open Hashing(再哈希法) vs Closed Hashing(链地址法)

part 7 C++标准库
  提供map容器,可以插入,删除,查找key-value pair,底层以平衡二叉搜索树的方式实现,根据key进行了排序。
  在C++11中,标准库添加了unordered_map,更符合Hash table的传统定义,平均查找时间O(1)

part 8 String
  在C语言中,字符串指的是一个以’\0’结尾的char数组。关于字符串的函数通常需要传入一个字符型指针。
  在C++中,String是一个类,并且可以通过调用类函数实现判断字符串长度,子串等等操作。

part 9 工具箱:C语言中String常用函数

char *strcpy ( char *destination, const char *source ); //copy source string to destination string
char *strcat ( char *destination, const char *source ); //Appends a copy of the source string to the destination string.
int strcmp ( const char *str1, const char *str2 );
char *strstr (char *str1, const char *str2 ); // Returns a pointer to the first occurrence of str2 in str1, or a NULL pointer if str2 is not part of str1.
size_t strlen ( const char *str ); // Returns the length of the C string str.
double atof (const char *str); // convert char string to a double
int atoi (const char *str); // convert char string to an int

part 10 工具箱:C++中String类常用函数
  String类重载了+, <, >, =, ==等运算符,故复制、比较、判断是否相等,附加子串等都可以用运算符直接实现。请参考( http://www.cplusplus.com/reference/string/string/)

size_t find (const string& str, size_t pos = 0) const; // Searches the string for the first occurrence of the str, returns index
string substr (size_t pos = 0, size_t len = npos) const; // Returns a newly constructed string object with its value initialized to a copy of a substring starting at pos with length len.
string &erase (size_t pos = 0, size_t len = npos); // erase characters from pos with length len
size_t length(); // Returns the length of the string, in terms of bytes

模式识别

  当遇到某些题目需要统计一个元素集中元素出现的次数,应该直觉反应使用 Hash Table,即使用 std::unordered_map 或 std::map:key 是元素,value 是出现的次数。特别地,有一些题目仅仅需要判断元素出现与否(相当于判断 value 是0还是1),可以用 bitvector,即 bitset,利用一个bit来表示当前的下标是否有值。

  

时间: 2024-10-17 04:11:42

算法原理与实践(数组和字符串)的相关文章

GeekBand-极客班-C++算法原理与实践-董飞-课程笔记(一)

GeekBand-极客班-C++算法原理与实践-董飞-课程笔记(一) 算法入门和编程风格 从一道入门题谈起 /*StrStr Returns the position of the first occurrence of string get in string source, or -1 if target is not part not of source. */ 另一篇博客:http://blog.csdn.net/lionpku/article/details/46713877 StrS

数据挖掘系列(8)朴素贝叶斯分类算法原理与实践

隔了很久没有写数据挖掘系列的文章了,今天介绍一下朴素贝叶斯分类算法,讲一下基本原理,再以文本分类实践. 一个简单的例子 朴素贝叶斯算法是一个典型的统计学习方法,主要理论基础就是一个贝叶斯公式,贝叶斯公式的基本定义如下: 这个公式虽然看上去简单,但它却能总结历史,预知未来.公式的右边是总结历史,公式的左边是预知未来,如果把Y看出类别,X看出特征,P(Yk|X)就是在已知特征X的情况下求Yk类别的概率,而对P(Yk|X)的计算又全部转化到类别Yk的特征分布上来. 举个例子,大学的时候,某男生经常去图

kmeans算法原理以及实践操作

kmeans一般在数据分析前期使用,选取适当的k,将数据聚类后,然后研究不同聚类下数据的特点. 算法原理: (1) 随机选取k个中心点: (2) 在第j次迭代中,对于每个样本点,选取最近的中心点,归为该类: (3) 更新中心点为每类的均值: (4) j<-j+1 ,重复(2)(3)迭代更新,直至误差小到某个值或者到达一定的迭代步数,误差不变. 空间复杂度o(N) 时间复杂度o(I*K*N) 其中N为样本点个数,K为中心点个数,I为迭代次数 为什么迭代后误差逐渐减小: SSE=  对于 而言,求导

算法原理与实践(栈与队列)

Stack & Queue 大纲 1. Stack介绍 2. Queue介绍 3. 例题分析 Stack A stack is a container of objects that are inserted and removed according to the last-in first-out (LIFO) principle Operations: Push O(1), Pop O(1), Top O(1) 栈的用途 可以用 Stack 作为辅助,实现深度优先算法(Depth firs

C语言 选择排序算法原理和实现 从数组中 找出最小的元素然后交换位置

#include <stdio.h> int main(void) { /* 选择排序算法 原理:从数组中 找出最小的元素然后交换位置: */ int a[10] = {9,5,10,7,2,3,1,6,8,4}; int i=0,j=0; int n = sizeof(a)/4; //外循环n-1轮 for(i=0;i<n-1;i++){ int pos = i;//始终指向最小的位置 for(j=i+1;j<n;j++){ if(a[j]<a[pos]){ pos = j

郑捷《机器学习算法原理与编程实践》学习笔记(第六章 神经网络初步)6.3 自组织特征映射神经网路(SMO)

具体原理网址:http://wenku.baidu.com/link?url=zSDn1fRKXlfafc_tbofxw1mTaY0LgtH4GWHqs5rl8w2l5I4GF35PmiO43Cnz3YeFrrkGsXgnFmqoKGGaCrylnBgx4cZC3vymiRYvC4d3DF3 自组织特征映射神经网络(Self-Organizing Feature Map.也称Kohonen映射),简称为SMO网络,主要用于解决模式识别类的问题.SMO网络属于无监督学习算法,与之前的Kmeans算

《机器学习算法原理与编程实践》学习笔记(二)

(上接第一章) 1.2 对象.矩阵与矢量化编程 1.2.1对象与维度(略) 1.2.2初识矩阵(略) 1.2.3矢量化编程与GPU运算(略) 1.2.4理解数学公式与NumPy矩阵运算 1.矩阵的初始化 #coding:utf-8 import numpy as np #导入NumPy包 #创建3*5的全0矩阵和全1的矩阵 myZero = np.zeros([3,5])#3*5的全0矩阵 print myZero myZero = np.ones([3,5])##3*5的全1矩阵 print

《机器学习算法原理与编程实践》学习笔记(三)

(上接第一章) 1.2.5 Linalg线性代数库 在矩阵的基本运算基础之上,NumPy的Linalg库可以满足大多数的线性代数运算. .矩阵的行列式 .矩阵的逆 .矩阵的对称 .矩阵的秩 .可逆矩阵求解线性方程 1.矩阵的行列式 In [4]: from numpy import * In [5]: #n阶矩阵的行列式运算 In [6]: A = mat([[1,2,3],[4,5,6],[7,8,9]]) In [7]: print "det(A):",linalg.det(A)

java常用的数组、字符串、集合操作以及数据结构与算法基本知识

java中常用封装的数组 .字符串. 集合来操作对象,但数据结构中常用的有栈和队列   数与图以及他们之间的排序,查找. 数组声明没有分配内存空间  只有创建或者是初始化时才分配,创建时把数组中的数据类型数据所在的内存空间首地址赋值给数组名,在创建每个对象时,都会给该对象分配地址和它存储的数据 .如变量    int arr[]; int arr[]={1,2,3};  arr=new int[10] ,int arr[][]={{1,9,7},{1,2,3}}  int arr[][]=new